每当说道Java垃圾回收,人们每次都会和C++做比较,本人没有很深入的了解过C++,也不知道c++的内存管理有多么的难,希望以后能有机会。
一、内存的分配垃圾回收,说的也就是对内存的管理,Java中内存主要分为6个部分1.寄存器:最快的存储区,有编译器根据需要进行分配,我们在程序中无法控制。2.栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中么事存放在堆(new出来的对象)或者常量池中(字符串常量对象存放在常量池中)。3.堆:存放所有new出来的对象。4.静态域:存放静态成员(static定义的)。5.常量池:存放字符串常量和基本类型常量(public static final)。6.非RAM存储:硬盘等永久存储空间。堆和栈是程序运行的关键,栈是运行时的的单位,而堆是存储的单位。栈中的数据大小和生命周期是可以确定的,当没有引用只想数据时,这个数据就会消失。堆中的对象则由垃圾回收器回收。二、什么是“垃圾”Java中是通过引用来和对象关联的,换句话说,操作对象是通过对象的引用来进行的。当一个对象没有任何引用是,说明这个对象基本不大可能在其他地方没用到,这种没有引用的对象就被看做为“垃圾”。但是这样就会产生一个问题,如果是循环引用,那么对象则不会被回收。三、垃圾回收机制现在Java所应用的垃圾回收机制是分代垃圾回收,当然垃圾回收机制也是一步一步发展过来的,从最初的引用计数,到标记-清除,再到复制,再到标记-压缩,最后到今天在用的分代垃圾回收,下面说一下每种回收机制的原理。引用计数:如果一个对象有一个引用,则增加一个计数,删除一个引用减少一个计数。垃圾收集时,只用收集计数为0的对象。这种算法无法处理循环引用的问题。
标记-清除:标记-清除回收分两个阶段,第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把没有标记的对象清除,此算法需要暂停整个应用,遍历整个堆消耗很慢,而且会使内存碎片化。
复制:次算法吧内存分为两个相等的区域,每次只使用一个区域。垃圾回收时,遍历当前使用区域,吧正在使用中的对象复制到另外一个区域。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后进行相应的内存整理,不会出现内存碎片化的问题。此算法,比较消耗内存,每次只用到内存的一半,内存利用效率低。
标记-压缩:整合了“标记-清除”和“复制”两个算法的优点。标记-压缩也分为两个阶段,第一阶段从根节点开始标记所有被引用的对象,第二阶段遍历整个堆,清除没有标记的对象并且把存活的对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的内存碎片化问题,同时也避免了“复制”时内存利用率不高的问题,但是并没有解决遍历堆内存,比较消耗时间的问题。
分代垃圾回收:分代垃圾回收是基于不同的对象的生命周期是不一样的,将堆内存分配为三个不同的代,年轻代、年老代、持久代。
年轻代:所有新生成的对象首先都是放在年轻代。年轻代分为三个区,一个Eden区,两个Survivor区(一般情况下是这三个区,大概比例8:1:1)。大部分对象在Eden区生成。当Eden区满后,进行垃圾回收Eden区,将还存活的对象复制到Survivor的其中一个区,当这个Survivor区满之后,进行垃圾回收Eden和满了的Survivor区,将存活的对象复制到另外一个Survivor区,当这个Survivor区满了的时候,从第一个Survivor中复制过来的对象会被复制到“年老代”。年老代:在年老代中存放的都是在年轻代中经历过多次垃圾回收依然存活的对象,年老代中存放的都是生命周期比较长的对象。当年老代内存满了之后,会进行垃圾回收,同时也会推动年轻代进行垃圾回收。持久代:持久代用于存放静态文件,如类和方法。四、垃圾回收存在的问题1,静态集合类对象HashMap、Vector等的使用最容易出现内存泄漏,这些静态变量的生命周期和应用程序一致,所有的对象不能被释放。Static Vector v = new Vector();for (int i = 1; i<100; i++) { Object o = new Object(); v.add(o); o = null;}如上面的代码,代码栈中存放在Vector对象的引用v和Object对象的引用o。在for循环中,我们不断的创建新的对象,然后将其添加到Vector对象中,之后将o引用置空。将o引用置空之后,会发生GC,GC发现代码栈中的v引用,然后往下跟踪,会发现v引用指向的事内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以不能回收在此循环之后,Object对象对程序已经没有任何作用,这样我们认为Java程序发生了内存泄漏。2.各种连接,数据库连接,网络连接,IO连接等没有显示的close关闭,不会被回收,导致内存泄漏。3.监听器的使用,在释放对象的同时,没用相应的删除监听器的时候也会导致内存泄漏。