各位小伙伴应该都上班了吧,好啦,大家把心收回来好好工作吧,今天冰河继续给大家分享一篇关于JVM的文章。
在《[JVM垃圾回收机制] 》一文中,我们知道了哪些类是需要清除的,那在java虚拟机中,他有哪些垃圾收集算法呢?
标记-清除算法就是,先标记哪些对象是存活的,哪些对象是可以回收的,然后再把标记为可回收的对象清除掉。
从下面的图可以看到,回收后,产生了大量的不连续的内存碎片,如果这个时候,有一个比较大的对象进来,却没有一个连续的内存空间给他使用,又会触发一次垃圾收集动作。
复制算法是通过两个内存空间,把一方存活的对象复制到另外一个内存空间上,然后再把自己的内存空间直接清理。标记后,此时情况如下:
然后再把左边的存活对象复制到右边:
复制完后,再清理自己的内存空间:
右边的空间开始回收,再复制到坐标,如此往复。这样就可以让存活的对象紧密的靠在一起,腾出了连续的内存空间。
缺点就是空间少了一半,这少了一半的空间用于复制存活的对象。但是在实际过程中,大部分的对象的存活时间都非常短,也就是说这些对象都可以被回收的。
比如说左边有100M空间,但是只有1M的对象是存活的,那我们右边就不需要100M来存放这个1M的存活对象。
因此我们的新生代是分成3个内存块的:Eden空间、From Survivor空间、To Survivor空间,他们的默认比例是8:1:1。
也就是说,平常的时候有Eden+Survivor=90M可以使用,10M用于存放存活对象(假设100M空间)。这个算法在《[JVM堆内存分配机制] 》一文中的对象优先在Eden分配中提过了。
除了新生代,老年代的内存也要清理的,但是上面的算法并不适合老年代。
因为老年代对象的生命周期都比较长,那就不能像新生代一样仅浪费10%的内存空间,而是浪费一半的内存空间。
标记-整理与标记-清除都是先标记哪些对象存活,哪些对象可以回收,不同的是他并没有直接清理可回收的对象,而是先把存活的对象进行移动,这样存活的对象就紧密的靠在一起,最后才把垃圾回收掉。
回收前,存活对象是不连续的。
回收中,存活对象是连续的。
回收后,回收垃圾对象。
由于每次GC,都会Stop The World,也就是说,此时虚拟机会把所有工作的线程都停掉,会给用户带来不良的体验及影响,所以我们要尽量减少GC的次数。
针对新生代,Minor GC触发的原因就是新生代的Eden区满了,所以为了减少Minor GC,我们新生代的内存空间不要太小。如果说我们给新生代的内存已经到达机器的极限了,那只能做集群了,把压力分担出去。
老年代的垃圾回收速度比新生代要慢10倍,所以我们也要尽量减少发生Full GC。
《[JVM堆内存分配机制] 》一文中我们提到了几种进入老年代的方式,我们看看这些是怎么处理的:
上面的方法,既有加大新生代的内存空间,也有加大Survivor空间,实时上,怎么优化,需要根据我们的实际场景来看,JVM的优化并没有银弹。
好了,今天就到这儿吧,我是冰河,我们下期见~~
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8