java推荐 内存的自动化整理 也就是自动化解决给对象分配内存以及回收对象的内存 ,这两个问题也是主要针对java的内存模型 堆 ;有效解决内存丢失等问题;
1.内存分类:
新生代:
eden内存 | 新建的对象存储的位置 |
survivor0 | 当eden内存空间存满之后就会将存活的对象进行复制进入survivor0空间,eden内存空间进行一次GC回收 |
survivor1 | 当Eden内存和survivor0都存满之后,就会将存活的对象复制进入survivor1空间,eden和survivor0都回收,survivor0就会执行survivor1一样的功能。 |
老年代:
存储存活期较长的对象,既然新生代分为了三个区,那新生代就有存满的时候,当新生代全部存满的时候,就会将新生代中存活的对象全部存入老年代,发生一次minorGC
有一些对象直接进入老年代,譬如一些大对象,譬如一些长的字符串
永久代:
存放一些常量,final关键字修饰的对象,或者变量
但是在jdk1.7之后永久代被取消,取而代之的是元空间
元空间:
元空间作用跟永久代相似,但是是存储在本地内存,受本地内存的限制
2.对象存活判断算法:
1.引用计数算法:
通过判断该对象的引用数量来判断这个对象是否可以回收,添加标志位如果有另外的引用就将标志位+1,否则-1;
引用计数收集器可以很快的执行,对于某些需要长时间执行的程序有很好的适应性,但是他有一个致命的缺点就是他无法解决两个对象之间相互引用的情况
2.可达性分析算法:
通过判断对象的引用链是否可达来判断这个对象是否可以回收,程序将所有引用关系看作是一张图,通过一系列的名为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。
如果GCRoot和某个对象之间没有任何的引用链链接,就将这个对象进行回收。
3.GC回收算法:
1.标记清除法
标记清除法分为标记和清除两个部分,这个算法将根集合进行扫描,并将存活的对象进行标记,然后再次扫描这个根集合将未被标记的对象进行回收。
缺点:
1.时间:耗费太多的时间,效率太低;
2.空间:清除的空间大多是不连续的小空间,如果想开辟一个大的对象空间,就要在进行一次minorGc
2.复制算法
该算法主要应用在新生代中,新生代的对象存活时间短,并且存活的对象所占的比例少,所以为新生代专门开发一种复制算法。
当eden内存空间满了以后,将Eden内存中存活的对象全部复制到survivor0内存空间中去,然后将eden内存空间全部清空,如果,survivor0内存空间和eden空间全部存储满了之后再将Eden空间和survivor0空间存活的对象复制进入survivor1内存空间,如果全都满了,那么就将存活的对象存入老年代内存空间中。
3.标记整理算法
该算法类似于标记清除算法,但是它在清除的时候会进行空间的重新排列,将剩余的空间重新排列起来,解决了标记清除算法的空间问题。
4.分代回收算法
这个算法主要就是说每个不同的代有自己的GC算法,新生代主要使用的就是复制算法,而老年代主要时候的就是标志清除以及标志整理算法。
垃圾回收的两种类型:
♦minorGC:对新生代进行回收,并不会影响老年代,由于新生代中对象的存活周期较短,所以monorGC会非常频繁,所以这里使用速度快效率高的算法。
♣FullGC:对整个内存区域进行回收,包括新生代和老年代,因为需要全部回收所以会需要较多的时间,应该避免fullGC的多次使用,导致Full GC的原因包括:老年代被写满、永久代(Perm)被写满和System.gc()
被显式调用等。
四、内存分配与回收策略
-
对象优先在Eden分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC。现在的商业虚拟机一般都采用复制算法来回收新生代,将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。 当进行垃圾回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor空间上,最后处理掉Eden和刚才的Survivor空间。(HotSpot虚拟机默认Eden和Survivor的大小比例是8:1)当Survivor空间不够用时,需要依赖老年代进行分配担保。
-
大对象直接进入老年代。所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。
-
长期存活的对象将进入老年代。当对象在新生代中经历过一定次数(默认为15)的Minor GC后,就会被晋升到老年代中。
-
动态对象年龄判定。为了更好地适应不同程序的内存状况,虚要求对象年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
五、垃圾收集器
1.CMS垃圾收集器:
初始标记是单线程的,同时这个cms使用的是标记清除算法,只适用于老年代。
2.G1垃圾收集器
G1收集器将整个堆分成了大小一致的空间Region,不同的空间就是不同的代
由于分成了一个个空间造成了一些空间浪费,但是它本质上就达到了局部压缩方案。
G1使用的就是分代收集算法,新生代:复制算法;老年代:标记清除/标记整理。
youngGc:新生代发生的垃圾回收
minedGc:收集整个新生代的同时收集一部分老年代(我看网上说的是jvm调优的时候可以自己设置这个阙值)
fullGc:就是全局收集
流程图类似于cms垃圾收集器,但是G1更注重于停顿时间,所以他添加一个setGoal方法,自定义停顿时间
但是在特定的停顿时间里面没法处理完所有事务,会先处理优先级高的,这个优先级就是看各个分区里面谁的垃圾比较多,优先清除垃圾多的分区所以起名叫Garbage First GC。
G1垃圾收集器里面含有一个RSet用来记录其他Region对于当前Region的引用
RSet有多个粒度
1.储存索引
2.储存Region的Id
3.储存引用个数
多个粒度可以在热点信息的引用时,减少RSet的占用空间。
问题:
1.在执行可达性分析的时候gcroot是怎么选择的?
虚拟机栈中引用的对象,方法区中静态引用的对象,蹦迪方法栈中的对象,方法区常量池中引用的对象。
2.什么时候选G1垃圾收集器,什么时候选择CMS垃圾回收器?
G1注重停顿时间,cms注重吞吐量,因为G1的创建就是为了替代或者说和Cms之间交换使用,所以我会选择G1,因为Cms有的G1也有,但是G1有的CMS没有
3.元空间和永久代的区别和优缺点?
永久代内存限制过大且jvm调优的时候过于麻烦,伴随着每次fullGc都容易报OOM的错,所以将永久代交给了本地储存也就变成了元空间
元空间不在jvm中而是在本地储存中且大小可以动态变更。