`
chenjili88
  • 浏览: 3610 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

JVM垃圾回收与内存分配浅析(二)

 
阅读更多

接上文:JVM垃圾回收与内存分配浅析(一)

现在的内存回收大部分都指的是在堆中的内存回收,因为其内存空间最大,对象实例频繁的创建、销毁就容易产生更多的内存碎片,那么首先我们就要检测堆中是否存在需要回收的垃圾,检测时检测的是对象是否已经死亡或者无其它引用关系,检测对象状态有有两种方式,一种是给对象添加一个引用计数器,增加一个引用时计数器+1,引用失效时,计数器-1,那么当计数器为0时,说明对象可以被回收,但是在JVM里没有采用这种算法,而是另外一种根搜索法,是通过一系列的叫做“GC ROOTS”的对象作为起始点,从这些点开始向下搜索引用链,当一个对象不能连接到GC ROOTS时,就判断它是“垃圾”,需要回收。而可以作为“GC ROOTS”的对象包含栈中的对象引用的对象、方法区里的类静态属性引用对象、方法区里的常量引用的对象、本地方法栈中的JNI方法引用的对象。

检测到了垃圾内存,那么接下来就要开始回收了,回收也有回收的算法,最简单的算法就是“标记-清除”,因为在查找垃圾内存时已经对对象做了标记,现在要做的就是把这些标记的消除掉就行了,但是还有一问题,就是这些垃圾内存地址可能不是连续的,这样的话清除掉以后,如果现在有一个大的对象需要分配给一些连接的内存地址,那么可能已有的内存空间就不能满足这个对象的要求了,于是又有人想出了另外一种方法,就是把内存空间一分为二,平常我们只使用其中的一半,如果其中一半内存空间中存在垃圾内存,就把这一半空间内的存活对象copy到另外一个空间,然后把这一半的空间内存全部清除就行了,这种方法显而易见我们的内存使用率太小了,才使用了50%,最后还有一种算法是“标记-整理”,和第一种类似,与之不同的是,它并不是直接清除掉垃圾内存的,而是把存活的对象都移向内存空间边界,这样的话,垃圾内存就都在边界之外了,现在只需要把边界外的内存清理掉就行了。

有了回收算法,我们是不是就可以可以回收了?但是我们要选择哪种算法好呢,人们又根据对象存活时间长短,把堆分为了新生代,老年代,从字面上看新代表时间短,老代表时间长,于是把对象存活时间短的对象(朝生夕死)放在新生代中,把对象存活时间长的放在老年代中,在新生代中,又把内存空间分为一块大的Eden空间与两块小的Survivor空间,然后把对象主要放在Eden与一块Survivor空间中,在存在垃圾内存时,采用复制算法,把存活的对象放入另外一个Survivor空间,清除掉Eden与Survivor空间,这样内存空间的利用率也提高了,效率也提高了,对于老年代则采用“标记-清除”或者“标记-整理”方法来回收内存。接下来就可以使用各种不两只的垃圾回收器来回收了。

内存回收说完了,那么内存的分配就是一个逆向的工程了,首先大部分的对象是分配到新生代的Eden空间的,对于大对象(大的数组或者字符串)则直接分配到老年代,如果初始空间不够的话,就会执行一次垃圾回收,那么又有一问题了如何判断一个对象是一个周期时间长的对象呢,因为初始对象都是在新生代中,如果经过一次内存回收之后,对象还存在,那么给它的标记+1,等累加到一定程度时,就会把这个对象移到老年代中,当然了还有一种动态对象年龄判定的策略,就是如果Survivor空间的同一年龄对象的年龄总和大于空间的一半,那么大于这个年龄的对象也会直接移到老年代。

通过了解JVM内存的分配与管理,有助于我们在实际开发遇到像内存溢出、泄漏时知道大概是哪里出的问题,并且跟踪调试。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics