什么是垃圾回收:在java语言诞生之前,大家都在写c,c++的程序,而此时存在一个很大的矛盾,就是c++这类语言创建对象需要不断地去开辟空间。不用的时候又需要去释放空间,既要写构造函数,还要写解析函数,直到之后发明了GC机制才解决了这一问题。
垃圾回收(GC):java进程在启动后会创建垃圾回收线程,来对内存中无用的垃圾进行回收。
什么是垃圾:无用的对象(堆),常量(常量池),类型(方法区的类信息)。
判断垃圾的算法:
1,引用计数算法:
在对象中添加一个引用计数器,每当有一个地方调动它的时候,计数器就加一,当引用失效时,计数器就减一,当计数器等于0时,这个对象就是不能被利用了,可以回收。
缺点:不能解决对象之间的相互引用问题。
2,可达性分析算法:
通过一系列的“GC Roots”对象作为起点,从这写节点开始往下搜索,搜索走过的路称为”GC Roots引用链“,当一个对象到GC Roots没有任何引用链连接时,这个对象就是不可用的。
垃圾回收的原理?怎么主动的通知虚拟机进行垃圾回收?什么时候会触发垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址,大小以及使用情况。
垃圾回收的时机:
1,显示的手动调用System.gc()
2,由JVM垃圾回收机制决定
垃圾回收的算法
1,标记清除算法(老年代的回收算法)
分为两个阶段:
(1)标记:标记无用的对象
(2)清除:清除标记的垃圾
缺陷:效率不高,存在内存碎片问题,
2,复制算法(新生代的回收算法)
将内存分为两部分,每次只用掉一部分,用完后,再将存活着的对象复制到另外一部分内存,然后把已经用掉的那部分清理掉。
优点:新生代中的对象,大部分是朝生夕死的,复制存货对象,性能很高。没有内存碎片问题
缺点:内存利用率不高,50%
3,标记整理算法(老年代的回收算法)
对象存活率较高是使用,优点:没有内存碎片
标记阶段和标记清除算法一样,而第二个阶段不同,而是将所有存活的对象都移到一端,而第二个阶段不同,而是将所有存活的对象都移到另一端,然后直接消除掉边界以外的内存。
4,分代收集算法
新生代因为每次回收的时候都有大量的对象死去,所以用复制算法,
老年代对象存活率高,同时也没有额外的空间为他担保,所以只能用标记清除或者标记整理算法。
垃圾收集器
gc中的并行和并发
并行:用户线程暂停,垃圾回收线程执行
并发:用户线程和垃圾回收线程同时执行
吞吐量:运行用户代码时间/运行用户代码时间+垃圾回收时间
用户体验优先:单次线程停顿时间短
吞吐量优先:用户线程总的时间短
1,Serial收集器:
1)是一个单线程的收集器
2)新生代
3)复制算法
2,ParNew收集器:
1)新生代
2)复制算法
3)Serial的多线程版本
4)搭配CMS收集器,用户体验优先的应用使用
3,Parallel Scavenge收集器
1)新生代
2)复制算法
3)搭配Parallel Old作为吞吐量优先的应用使用
4)可控制吞吐量
5)自适应的调节策略:部分JVM参数,可以动态设置
4,Serial Old收集器
1)Serial老年代版本
2)单线程
3)标记整理算法
5,Paeallel Old收集器
1)标记整理算法
2)吞吐量优先
6,CMS收集器
1)并发收集,低停顿
2)标记清除算法
4)4个阶段
1,初始阶段:只标记GC Roors能直接关联到的对象
2,并发阶段:进行GC Roots Tracing,速度慢
3,重新标记:为了修正并发标记期间用户程序运行而导致标记产生
4,并发消除:并发消除阶段会消除对象
缺陷:
(1)用户体验优先,相对应吞吐量下降
(2)浮动垃圾问题,老年代预留空间给浮动垃圾,预留空间不足,出现并发模式错误,解决方案:当先gc还在执行,又提前出发了一次major gc ,存在内存碎片问题
7,G1收集器(唯一全区域的垃圾回收器)
标记整理算法,可预测停顿,
新生代,老年代
新生代:新创建出来的对象,新生代分为一个伊甸区(Eden)和两个幸存者区(survivor分别叫from和to),默认比例为8:1:1,这个比例可以修改。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。
老年代:老年代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁(譬如可能几个小时一次)。老年代主要采用压缩的方式来避免内存碎片(将存活对象移动到内存片的一边,也就是内存整理)。当然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能会不进行压缩。
一个对象的一生
我是一个普通的Java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。在年老代里,我生活了20年(每次GC加一岁),然后被回收。
为什么要分代
分代的唯一好处就是优化gc性能,不分代的话我们把所有对象都放在一起,gc时我们要找到哪些对象没用,我们就要对堆的所有区域进行扫描,这样效率就大大降低了,而我们很多的对象都是朝生夕死的,如果分代的话,我们把新创建的对象放到某一块地方,当gc时先把这块地方的对象进行回收,效率就提高了
垃圾回收日志
日志级别从低到高:Trace,Debug,Info,Warning,Error,Off共6种–默认为Info
命令
查看gc基本信息:
- -XX:+PrintGC——JDK9之前使用
- -Xlog:gc——JDK0之后使用
查看gc详细信息:
- -XX:+PrintGCDetails——JDK9之前
- -Xlog:gc*——JDK9之后
查看前后堆和方法区可用容量的变化:
- -XX:+PrintHeapAtGC
- -Xlog:gc+heap=debug
查看gc过程中用户线程并发时间以及停顿的时间
- -XX:+PrintGCApplicationConcurrentTime
- -XX:+PrintGCApplicationStoppedTime
- -Xlog:safepoint
查看熬过收集后剩余对象的年龄分布信息
- -XX:+PrintTenuring-Distribution
- -Xlog:gc+age=trace
元空间/方法区
- jdk1.7的方法区在gc中一般称为永久代
- jdk1.8的元空间存在于本地内存中,gc也就是对元空间进行垃圾回收表
-方法区或者元空间的垃圾回收主要分为两个部分:废弃常量和无用类;
堆
java堆是垃圾收集器的主要管理场所 ,因此也被称为gc堆
由于现在的垃圾回收器基本都是采用分代收集,所以java堆还*可以细分为:
1,新生代*
- 新生代的垃圾回收称为:Minor GC或者Young GC;
- 新生代的垃圾回收指发生在新生代的垃圾回收,因为新生代对象具有朝生夕灭的特性,所以Minor GC非常频繁,一般回收动作也比较快;
2,老年代
-
老年代垃圾回收称为:Major GC
-
老年代垃圾回收指发生在老年代上的垃圾回收。出现了Major GC,一般都会伴随着至少一次的Minor GC;
-
Major GC 一般比Minor GC慢十倍以上