JAVA对象的前世今生

我的困惑

最近做的一个项目涉及到数据量比较大,涉及一个约200G大的excel表格,是不可能在内存里打开的.我要做的事情其中一步是遍历整个大表,拿出其中一部分数据.我计算了一下我需要的内存大小,应该是在6-8G,再考虑到我机器的性能(16g内存),想想应该可以用一个hashmap来存我想要的数据.于是开心的写出了程序,用一个全局的hashmap变量来保存数据.结果每次是运行了差不多两个小时的时候程序就崩了,出现的错误当然是内存溢出:

java.lang.outofMemery:gc overhead limit exceeded
或者:
java.lang.outofMemery:Java heap space

我试了修改eclipse.ini文件,增加了jvm的内存分配:

-Xms1024m
-Xmx8192m
-XX:PermSize=512m
-XX:MaxPermSize=2048m  

结果依然没有解决问题,有的同学说,eclipse.ini只是为jvm分配内存的.而你真正运行的这个java程序不一定就分配到了那么多的内存,因为可能你正在运行好几个java程序,他们要一起竞争jvm内存.如果想要为我的java程序单独分配内存,需要为它添加启动参数,具体是这么做的:

Run-->Run Configuration-->(x)=arguments
VM arguments下输入
-Xms1024m
-Xmx8192m
以及其他你想要的参数,然后点击aplly.

如下图.

看到有人说可以用:

-XX:-UseGCOverheadLimit

来Disabling the error check altogether.但又出现:

java.lang.outofMemery:Java heap space

的错误.所以我想问题应该不仅仅是内存不够用那么简单,至少通过增加jvm内存的方式来解决似乎让人看不到尽头.于是我好好学习了一下java的GC机制,看到底是什么地方导致了内存溢出.

堆与栈:生存空间

  • 堆(heap):对象的生存空间.
  • 栈(stack):方法调用和局部变量.
  • 实例变量 :被声明在类里面的变量,它代表某个独立对象的"字段",存在于所属的对象中.
  • 局部变量 :声明在方法中,生命周期只限于方法被放在栈上的这段期间.也就是方法被调用到结束的时间.

注意到一个事实,对象永远存在于堆上,如果我们在方法中定义了一个类类型变量,并赋值给它一个对象,其实我们只是定义了该对象的一个引用,引用变量本身是放在栈上面的,它所引用的对象还是在堆里.

当你调用一个方法时,该方法会放在调用栈的栈顶,它带有方法的状态,包括执行到那一行程序以及所有的局部变量的值,如果在方法中又调用了其他的方法,那么被调用的方法又被推到了堆栈顶,直到执行完毕被释放,回到上一个方法.

构造函数的执行顺序

public class Animal{
    public Animal(){
        System.out.println("making an animal");}
}

public class Hippo extends Animal{
    public Hippo(){
        System.out.println("making a hippo");}
}

public class TestHippo{
    public static void main()(String args[]){
        System.out.println("making a hippo");
        Hippo h = new Hippo();}
}

所有的java对象都有一个共同的祖先:Object类. 所以执行顺序是:

                       Object()
          Animal()     Animal()       Animal()
Hippo()   Hippo()      Hippo()        Hippo()      Hippo()

调用Hippo()时,会调用父类的构造函数,依次上升,直到Object类,然后依次返回,直到Hippo类.

如何调用父类的构造函数

可以使用super()函数,注意super();必须是你的构造函数的第一行代码.如果你没有显示使用super函数,编译器会为你生成一个super,使用父类的无参数构造函数,当然你完全可以使用父类的带参数的构造函数,只要往super里传入相应类型的值就可以了.

public class Hippo extends Animal{
    public Hippo(String name){
        super(name);}
}

引用变量与对象

  • 除非有对象的引用,否则该对象一点意义都没有.
  • 如果你无法取得对象的引用,则此对象只是浪费空间罢了.
  • 一旦对象无法取得,GC会知道该怎么做,那种对象迟早会葬送在垃圾手机器的手上.

有三种方法可以释放对象的引用:

  • 引用永久性的离开它的作用域
void go(){
Life z = new Life();
} 
z会在方法结束时消失.
  • 引用被赋值到其他对象上
Life z = new Life();
z = new Life();
第一个对象会在z被赋值到别处时挂掉.
  • 直接将引用设定为null
Life z = new Life();
z = null;
第一个对象会在z被设定为null时挂掉.

Comments !