讲JVM垃圾回收,我们从以下7个部分来总结一下:
什么是垃圾回收,为什么要回收
垃圾怎么定义
定义垃圾的2种算法
垃圾收集的4种算法
垃圾收集器种类和选择
full gc时堆空间不足,一定会OOM吗
有了垃圾回收,java会不会内存泄露
1.什么是垃圾回收
垃圾回收是为了防止java内存泄露,主要是防止堆内存的内存泄露。
------------------------------------------------分割线-------------------------------------------------------
2.什么是内存泄露
内存泄露是jvm堆内存中死亡对象或没有引用的对象占据的内存空间没有释放,导致这部分空间造成浪费无法使用的现象。
------------------------------------------------分割线-------------------------------------------------------
3.理解四种对象引用
java的引用分为4种引用:
1)强引用:比如 Test test = new Test();这里的test就是强引用,只要引用在,对象就不会被回收
2)软引用:如果内存比较紧张,可能会回收掉弱引用对象,这类对象不是必需对象
3)弱引用:非必需对象,只要触发gc,就会被回收,生命周期为2次gc之间
4)虚引用:再对象被回收时,收到一个系统通知,一般没啥用
------------------------------------------------分割线-------------------------------------------------------
4.怎么定义垃圾
要定义对象是否是垃圾,就是要判断该对象有没有指针指向它,换句话说,如果一个对象没有引用指向它,那这个对象在堆中就可以被定义为垃圾。我们来写一个示例代码来看一下:
首先定义一个类A:
package com.test.main;
public class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
再来定义一个测试类:默认jvm
package com.test.main;
import java.util.ArrayList;
import java.util.List;
public class RubbishTest {
public static void main(String[] args) {
while(true){
A instanceA = new A();
}
System.out.println("123");
}
}
看代码中,在一个while死循环中,不停的去创建A的对象实例,你们猜会不会出现堆溢出?
答案是:不会。
原因:虽然while死循环,不停的在创建A对象,但是A对象创建完成之后,没有任何地方引用instanceA,所以instanceA创建完成之后会被回收,instanceA就被定义为垃圾对象。
注意:如果jvm设置堆内存很小的时候,不停的new 对象还是可能会造成堆溢出。
我们来运行该程序时的gc情况:
eden区一直没有增长,对象没有引用被回收,此时死循环中的对象实例全部属于"垃圾"。
------------------------------------------------分割线-------------------------------------------------------
定义垃圾的2种算法
1.引用计数法
该算法在对象被创建存在引用的时候,在对象头空间中记录一个数值,代表当前对象引用数为1,当引用被释放,引用数会减少1,当引用数=0的时候,标记该对象为垃圾对象;
该算法已经被放弃,原因是因为:如果存在相互引用,那么对象被创建的时候,初始引用数为2,即使对象直接赋值为null,引用数依然不为0,无法被回收;
------------------------------------------------分割线-------------------------------------------------------
2.可达性分析算法
提出一个概念,名为Gc Roots;
Gc Roots对象包括以下:
1)虚拟机栈中引用的对象
2)方法区中静态属性引用的对象
3)方法区中常量引用的对象
4)native方法引用的对象
从引用本身出发,往后查找,能够找到最终引用的对象,代表该对象可达,不进行垃圾回收;
如果不可达,则定义为垃圾
------------------------------------------------分割线-------------------------------------------------------
垃圾收集的4种算法
我们上面已经说了2种怎么定义一个垃圾,现在垃圾被定义好了,如果从堆中进行gc收集呢?
1)标记清除法
该算法将定义为垃圾的对象进行标记,然后对这些打了标记的对象进行内存清理,会产生大量的不连续的内存碎片,就是一块儿内存有对象,一块儿内存没对象,互相紊乱交叉,不利用后续使用;
2)复制回收算法
该算法将内存一分为二,每次将需要收集的一块内存进行回收,剩余对象转移到另一块内存,那么当前这块儿内存就会被空闲出来,没有内存碎片;
缺点:代价有点大,每次只能使用一半的内存;
3)标记整理算法
该算法前面跟标记清除算法一样,先对垃圾进行标记,然后把标记过的垃圾进行移动,移动到同一侧进行回收,能够解决内存碎片问题,也能解决空间不利用的问题,但是内存地址移动整理过程,太过频繁,效率比较低;
4)分代收集算法
分代收集算法涉及到java内存模型,jvm将堆内存分为年轻代和老年代,同时年轻代分为2个survivor区,被称为from和to区,又称s0和s1区;这里可以参考我的jvm内存模型篇:jvm内存模型笔记
该算法,新生对象会进入eden区,进行垃圾收集时,会把剩余未清理的对象转移到s0区,再下一次,会把eden和from区同时转移到s1区,如此反复,来回在s0和s1进行复制交换,当s0和s1放不下,或者满足一定条件,比如gc分代年龄超过限制15次(默认值,可设置),就会将这些存活对象移到老年代,老年代再使用对应收集器进行收集;
这里有几个问题:
1:为什么需要survivor区?
答:起到缓冲作用,因为对象进行了一次survivor缓冲,在这个复制交换过程中,有可能对象会被回收掉,那么就减少了老年代gc的次数(如果从eden区直接到old区,老年代gc不是很频繁吗)
2:survivor为什么要分成2个?
答:其实默认是2个,也可以指定多个。为什么要2个,假设只有1个的话,从eden区到survivor区,survivor区如果有对象存活比较久,当垃圾对象被回收,存活对象还继续在survivor,那不是跟标记清除算法一样,又存在内存碎片了吗
------------------------------------------------分割线-------------------------------------------------------
垃圾收集器种类和选择
分代收集,代表了年轻代和老年代使用不同的收集算法,使用不同的收集器
收集器的种类:
1)Serial收集器:单线程收集器,采用单线程-复制回收算法,用于年轻代;
2)Serial Old收集器:单线程收集器,采用单线程-标记整理算法,用于老年代;
3)ParNew收集器:多线程版的Serial收集器,采用多线程-复制回收算法,用于年轻代;
4)Parallel Scavenge收集器:多线程收集器,采用多线程-复制回收算法,注重吞吐量,比如虚拟机运行了10分钟,有1分钟在进行垃圾收集,那么吞吐量就是99%,也叫吞吐量优先收集器,该收集器是-server模式下的虚拟机默认垃圾收集器,用于年轻代;
5)Parallel Old收集器:Parallel Scavenge的老年代版本,采用多线程-标记整理算法,用于老年代
6)CMS收集器:全称Conrrurent Mark Sweep,多线程收集器,采用多线程-标记清除算法,用于老年代;
年轻代和老年代搭配使用规则,如下图:
------------------------------------------------分割线-------------------------------------------------------
full gc没有可回收对象或者空间不足时必定会oom吗
答案:不一定。
我们来一段代码,同时设置一下jvm参数:如下:
这里的jvm设置参数为:
-Xms900m -Xmx900m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log
package com.test.main;
import java.util.ArrayList;
import java.util.List;
public class RubbishTest {
static class A{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
try {
List<A> list = new ArrayList<A>();
while (true){
A a = new A();
list.add(a);
System.out.println(System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我们来看一下jvm回收情况:
再来看一下线程dump日志:
Thread 14 "Finalizer": (state = BLOCKED)
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Thread 13 "Reference Handler": (state = BLOCKED)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
Thread 1 "main": (state = BLOCKED)
at java.nio.CharBuffer.wrap(CharBuffer.java:369)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:751)
at com.test.main.RubbishTest.main(RubbishTest.java:25)
我们发现main线程处于阻塞状态,再来看一下CPU情况:
我们再来看一下gc日志:
2020-10-13T11:55:07.501+0800: 1485.310: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585039K->585037K(614400K)] 811343K->811341K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4311508 secs] [Times: user=18.81 sys=0.17, real=2.43 secs]
2020-10-13T11:55:09.933+0800: 1487.741: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585039K->585037K(614400K)] 811343K->811341K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4080620 secs] [Times: user=19.17 sys=0.06, real=2.41 secs]
2020-10-13T11:55:12.341+0800: 1490.149: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585039K->585037K(614400K)] 811343K->811341K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4003168 secs] [Times: user=18.89 sys=0.13, real=2.40 secs]
2020-10-13T11:55:14.742+0800: 1492.550: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585039K->585037K(614400K)] 811343K->811341K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4072049 secs] [Times: user=18.92 sys=0.19, real=2.41 secs]
2020-10-13T11:55:17.150+0800: 1494.957: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585040K->585038K(614400K)] 811344K->811342K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4165598 secs] [Times: user=18.88 sys=0.17, real=2.42 secs]
2020-10-13T11:55:19.566+0800: 1497.374: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585040K->585038K(614400K)] 811344K->811342K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4496853 secs] [Times: user=19.36 sys=0.06, real=2.45 secs]
2020-10-13T11:55:22.016+0800: 1499.824: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585040K->585038K(614400K)] 811344K->811342K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4136350 secs] [Times: user=18.77 sys=0.09, real=2.41 secs]
2020-10-13T11:55:24.430+0800: 1502.238: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585040K->585038K(614400K)] 811344K->811342K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4035978 secs] [Times: user=19.11 sys=0.20, real=2.40 secs]
2020-10-13T11:55:26.833+0800: 1504.642: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585040K->585038K(614400K)] 811344K->811342K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4074083 secs] [Times: user=18.75 sys=0.19, real=2.41 secs]
2020-10-13T11:55:29.242+0800: 1507.050: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585041K->585039K(614400K)] 811345K->811343K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.3977490 secs] [Times: user=18.86 sys=0.17, real=2.40 secs]
2020-10-13T11:55:31.639+0800: 1509.448: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585041K->585039K(614400K)] 811345K->811343K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4077769 secs] [Times: user=18.64 sys=0.09, real=2.41 secs]
2020-10-13T11:55:34.047+0800: 1511.856: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585041K->585039K(614400K)] 811345K->811343K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4109829 secs] [Times: user=18.83 sys=0.13, real=2.41 secs]
2020-10-13T11:55:36.458+0800: 1514.267: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585041K->585039K(614400K)] 811345K->811343K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4132171 secs] [Times: user=18.81 sys=0.19, real=2.41 secs]
2020-10-13T11:55:38.872+0800: 1516.680: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585041K->585039K(614400K)] 811345K->811343K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4288084 secs] [Times: user=18.88 sys=0.22, real=2.43 secs]
2020-10-13T11:55:41.301+0800: 1519.109: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585042K->585040K(614400K)] 811346K->811344K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4301624 secs] [Times: user=18.92 sys=0.20, real=2.43 secs]
2020-10-13T11:55:43.731+0800: 1521.540: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585042K->585040K(614400K)] 811346K->811344K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4188353 secs] [Times: user=19.09 sys=0.17, real=2.42 secs]
2020-10-13T11:55:46.151+0800: 1523.959: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585042K->585040K(614400K)] 811346K->811344K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4330812 secs] [Times: user=19.26 sys=0.20, real=2.43 secs]
2020-10-13T11:55:48.584+0800: 1526.392: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585042K->585040K(614400K)] 811346K->811344K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4293412 secs] [Times: user=19.20 sys=0.20, real=2.43 secs]
2020-10-13T11:55:51.013+0800: 1528.822: [Full GC [PSYoungGen: 226304K->226304K(267264K)] [ParOldGen: 585042K->585040K(614400K)] 811346K->811344K(881664K) [PSPermGen: 3256K->3256K(21504K)], 2.4657140 secs] [Times: user=19.47 sys=0.20, real=2.47 secs]
2020-10-13T11:55:53.479+0800: 1531.287: [Full GC
这里通过GC日志可以看出来,jvm一直在努力的full gc,但是发现没有可以回收的对象,也没有抛出一个OOM异常,这就是所谓的内存泄露骗局**,发生原因是:eden区申请空间失败,转移至老年代,发生老年代空间不足,从而触发full gc,full gc无法回收堆空间,但是又没有达到超过提示OOM的范围(默认=2),这里我们根据gc日志来计算一下,占用空间=811344/881664=92%,剩余8%,大于默认的2%,这种情况下jvm会一直尝试full gc,但是不会抛出OOM;**
-------我们继续调整jvm参数,缩小到300M;-------
这里的jvm设置参数为:
-Xms300m -Xmx300m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log
执行结果:
"C:\Program Files\Java\jdk1.7.0_71\bin\java.exe" -Xms300m -Xmx300m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log "-javaagent:C:\IntelliJ IDEA2019.2.4\lib\idea_rt.jar=61101:C:\IntelliJ IDEA2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;E:\mytestProject\springtest\target\classes;D:\oms_resp\org\springframework\spring-core\4.3.19.RELEASE\spring-core-4.3.19.RELEASE.jar;D:\oms_resp\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\oms_resp\org\springframework\spring-beans\4.3.19.RELEASE\spring-beans-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context\4.3.19.RELEASE\spring-context-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-expression\4.3.19.RELEASE\spring-expression-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context-support\4.3.19.RELEASE\spring-context-support-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aop\4.3.19.RELEASE\spring-aop-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aspects\4.3.19.RELEASE\spring-aspects-4.3.19.RELEASE.jar;D:\oms_resp\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar;D:\oms_resp\org\aspectj\aspectjrt\1.9.4\aspectjrt-1.9.4.jar" com.test.main.RubbishTest
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at com.test.main.RubbishTest.main(RubbishTest.java:23)
Process finished with exit code 1
查看GC日志:
2020-10-13T14:02:39.849+0800: 0.134: [GC [PSYoungGen: 76800K->12787K(89600K)] 76800K->51795K(294400K), 0.0404025 secs] [Times: user=0.28 sys=0.03, real=0.04 secs]
2020-10-13T14:02:39.899+0800: 0.185: [GC [PSYoungGen: 89587K->12784K(89600K)] 128595K->99716K(294400K), 0.0556522 secs] [Times: user=0.63 sys=0.00, real=0.06 secs]
2020-10-13T14:02:39.967+0800: 0.252: [GC [PSYoungGen: 89584K->12768K(89600K)] 176516K->176643K(294400K), 0.0868302 secs] [Times: user=0.84 sys=0.05, real=0.09 secs]
2020-10-13T14:02:40.054+0800: 0.339: [Full GC [PSYoungGen: 12768K->0K(89600K)] [ParOldGen: 163875K->152278K(204800K)] 176643K->152278K(294400K) [PSPermGen: 3237K->3236K(21504K)], 1.2966809 secs] [Times: user=5.44 sys=0.08, real=1.30 secs]
2020-10-13T14:02:41.356+0800: 1.641: [GC [PSYoungGen: 28878K->12800K(89600K)] 181157K->181030K(294400K), 0.1553801 secs] [Times: user=1.56 sys=0.00, real=0.15 secs]
2020-10-13T14:02:41.511+0800: 1.796: [Full GC [PSYoungGen: 12800K->0K(89600K)] [ParOldGen: 168230K->180866K(204800K)] 181030K->180866K(294400K) [PSPermGen: 3236K->3236K(21504K)], 1.3299680 secs] [Times: user=6.70 sys=0.03, real=1.33 secs]
2020-10-13T14:02:42.852+0800: 3.137: [Full GC [PSYoungGen: 76800K->16895K(89600K)] [ParOldGen: 180866K->204715K(204800K)] 257666K->221610K(294400K) [PSPermGen: 3236K->3236K(21504K)], 0.7804965 secs] [Times: user=5.39 sys=0.08, real=0.78 secs]
2020-10-13T14:02:43.640+0800: 3.925: [Full GC [PSYoungGen: 66503K->66288K(89600K)] [ParOldGen: 204715K->204715K(204800K)] 271218K->271004K(294400K) [PSPermGen: 3236K->3236K(21504K)], 1.3011720 secs] [Times: user=10.70 sys=0.09, real=1.30 secs]
2020-10-13T14:02:44.941+0800: 5.226: [Full GC [PSYoungGen: 66288K->66288K(89600K)] [ParOldGen: 204715K->204697K(204800K)] 271004K->270986K(294400K) [PSPermGen: 3236K->3236K(21504K)], 0.8911681 secs] [Times: user=6.94 sys=0.09, real=0.89 secs]
Heap
PSYoungGen total 89600K, used 68737K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
eden space 76800K, 89% used [0x00000000f9c00000,0x00000000fdf20780,0x00000000fe700000)
from space 12800K, 0% used [0x00000000ff380000,0x00000000ff380000,0x0000000100000000)
to space 12800K, 0% used [0x00000000fe700000,0x00000000fe700000,0x00000000ff380000)
ParOldGen total 204800K, used 204697K [0x00000000ed400000, 0x00000000f9c00000, 0x00000000f9c00000)
object space 204800K, 99% used [0x00000000ed400000,0x00000000f9be6680,0x00000000f9c00000)
PSPermGen total 21504K, used 3267K [0x00000000e8200000, 0x00000000e9700000, 0x00000000ed400000)
object space 21504K, 15% used [0x00000000e8200000,0x00000000e8530f80,0x00000000e9700000)
这里的老年代放不下eden区gc的对象,抛出了OOM;我们继续验证一下,直接指定堆空闲的临界值为25;
我们来通过参数来设置一下空闲堆大小的值:-XX:GCHeapFreeLimit
如下设置jvm参数:
-Xms900m -Xmx900m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log **-XX:GCHeapFreeLimit=25**
-XX:GCHeapFreeLimit=25;代表剩余堆空间如果不足总大小的25%,那么就会抛出错误
我们再次运行,结果如下:
"C:\Program Files\Java\jdk1.7.0_71\bin\java.exe" -Xms900m -Xmx900m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log -XX:GCHeapFreeLimit=25 "-javaagent:C:\IntelliJ IDEA2019.2.4\lib\idea_rt.jar=61267:C:\IntelliJ IDEA2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;E:\mytestProject\springtest\target\classes;D:\oms_resp\org\springframework\spring-core\4.3.19.RELEASE\spring-core-4.3.19.RELEASE.jar;D:\oms_resp\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\oms_resp\org\springframework\spring-beans\4.3.19.RELEASE\spring-beans-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context\4.3.19.RELEASE\spring-context-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-expression\4.3.19.RELEASE\spring-expression-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context-support\4.3.19.RELEASE\spring-context-support-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aop\4.3.19.RELEASE\spring-aop-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aspects\4.3.19.RELEASE\spring-aspects-4.3.19.RELEASE.jar;D:\oms_resp\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar;D:\oms_resp\org\aspectj\aspectjrt\1.9.4\aspectjrt-1.9.4.jar" com.test.main.RubbishTest
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.test.main.RubbishTest.main(RubbishTest.java:23)
Process finished with exit code 1
这里抛出了GC错误,超过了GC最大限制;
我们来看一下GC日志:
2020-10-13T14:09:13.220+0800: 0.194: [GC [PSYoungGen: 230400K->38399K(268800K)] 230400K->148671K(883200K), 0.1209445 secs] [Times: user=0.92 sys=0.02, real=0.12 secs]
2020-10-13T14:09:13.365+0800: 0.340: [GC [PSYoungGen: 204502K->38368K(268800K)] 314774K->271554K(883200K), 0.1493940 secs] [Times: user=1.22 sys=0.02, real=0.15 secs]
2020-10-13T14:09:13.598+0800: 0.572: [GC [PSYoungGen: 268768K->38368K(268800K)] 623640K->542848K(883200K), 1.0427987 secs] [Times: user=9.20 sys=0.05, real=1.04 secs]
2020-10-13T14:09:14.641+0800: 1.615: [Full GC [PSYoungGen: 38368K->0K(268800K)] [ParOldGen: 504480K->488097K(614400K)] 542848K->488097K(883200K) [PSPermGen: 3237K->3236K(21504K)], 3.9514668 secs] [Times: user=15.28 sys=0.08, real=3.95 secs]
2020-10-13T14:09:18.612+0800: 5.586: [GC [PSYoungGen: 121758K->38400K(268800K)] 609856K->609457K(883200K), 0.5928956 secs] [Times: user=5.69 sys=0.02, real=0.59 secs]
2020-10-13T14:09:19.205+0800: 6.179: [Full GC [PSYoungGen: 38400K->0K(268800K)] [ParOldGen: 571057K->609020K(614400K)] 609457K->609020K(883200K) [PSPermGen: 3236K->3236K(21504K)], 4.4758163 secs] [Times: user=21.38 sys=0.16, real=4.48 secs]
2020-10-13T14:09:23.710+0800: 10.684: [Full GC [PSYoungGen: 230400K->230399K(268800K)] [ParOldGen: 609020K->487334K(614400K)] 839420K->717734K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.1423580 secs] [Times: user=16.30 sys=0.03, real=2.14 secs]
2020-10-13T14:09:25.853+0800: 12.827: [Full GC [PSYoungGen: 230400K->230399K(268800K)] [ParOldGen: 487334K->487334K(614400K)] 717734K->717734K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.2490204 secs] [Times: user=16.31 sys=0.09, real=2.25 secs]
2020-10-13T14:09:28.102+0800: 15.076: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487334K->487334K(614400K)] 717734K->717734K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0458826 secs] [Times: user=16.00 sys=0.17, real=2.05 secs]
2020-10-13T14:09:30.148+0800: 17.122: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487335K->487335K(614400K)] 717735K->717735K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0365295 secs] [Times: user=15.92 sys=0.09, real=2.04 secs]
2020-10-13T14:09:32.185+0800: 19.159: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487336K->487336K(614400K)] 717736K->717736K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0638299 secs] [Times: user=16.41 sys=0.11, real=2.06 secs]
2020-10-13T14:09:34.248+0800: 21.223: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487337K->487337K(614400K)] 717737K->717737K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0800033 secs] [Times: user=16.14 sys=0.14, real=2.08 secs]
2020-10-13T14:09:36.328+0800: 23.303: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487338K->487338K(614400K)] 717738K->717738K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0775493 secs] [Times: user=15.98 sys=0.14, real=2.08 secs]
2020-10-13T14:09:38.406+0800: 25.381: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487339K->487339K(614400K)] 717739K->717739K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0283449 secs] [Times: user=15.75 sys=0.17, real=2.03 secs]
2020-10-13T14:09:40.434+0800: 27.409: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487340K->487340K(614400K)] 717740K->717740K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0488832 secs] [Times: user=15.92 sys=0.16, real=2.05 secs]
2020-10-13T14:09:42.484+0800: 29.458: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487341K->487341K(614400K)] 717741K->717741K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.1725884 secs] [Times: user=16.39 sys=0.09, real=2.17 secs]
2020-10-13T14:09:44.657+0800: 31.631: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487342K->487342K(614400K)] 717742K->717742K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0455300 secs] [Times: user=15.75 sys=0.20, real=2.04 secs]
2020-10-13T14:09:46.702+0800: 33.677: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487343K->487343K(614400K)] 717743K->717743K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0976759 secs] [Times: user=16.55 sys=0.11, real=2.10 secs]
2020-10-13T14:09:48.800+0800: 35.775: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487344K->487344K(614400K)] 717744K->717744K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0533228 secs] [Times: user=15.63 sys=0.17, real=2.05 secs]
2020-10-13T14:09:50.853+0800: 37.828: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487345K->487345K(614400K)] 717745K->717745K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0544912 secs] [Times: user=15.95 sys=0.16, real=2.06 secs]
2020-10-13T14:09:52.908+0800: 39.883: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487346K->487346K(614400K)] 717746K->717746K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0304987 secs] [Times: user=16.13 sys=0.06, real=2.03 secs]
2020-10-13T14:09:54.939+0800: 41.913: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487347K->487347K(614400K)] 717747K->717747K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.0330014 secs] [Times: user=15.86 sys=0.23, real=2.03 secs]
2020-10-13T14:09:56.972+0800: 43.946: [Full GC [PSYoungGen: 230400K->230400K(268800K)] [ParOldGen: 487348K->487330K(614400K)] 717748K->717730K(883200K) [PSPermGen: 3236K->3236K(21504K)], 2.1918866 secs] [Times: user=16.78 sys=0.11, real=2.19 secs]
还是来计算一下堆剩余空间大小,占用空间=717730/883200=81%,剩19%,小于上面jvm设置的25%,所以就抛出了OOM错误;
我们回到上面猜测的堆设置300m的设置,指定-XX:GCHeapFreeLimit=25;如下jvm设置:
-Xms300m -Xmx300m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log -XX:GCHeapFreeLimit=25
继续运行结果如下:
"C:\Program Files\Java\jdk1.7.0_71\bin\java.exe" -Xms300m -Xmx300m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:d://gc2.log -XX:GCHeapFreeLimit=25 "-javaagent:C:\IntelliJ IDEA2019.2.4\lib\idea_rt.jar=63014:C:\IntelliJ IDEA2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;E:\mytestProject\springtest\target\classes;D:\oms_resp\org\springframework\spring-core\4.3.19.RELEASE\spring-core-4.3.19.RELEASE.jar;D:\oms_resp\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\oms_resp\org\springframework\spring-beans\4.3.19.RELEASE\spring-beans-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context\4.3.19.RELEASE\spring-context-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-expression\4.3.19.RELEASE\spring-expression-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-context-support\4.3.19.RELEASE\spring-context-support-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aop\4.3.19.RELEASE\spring-aop-4.3.19.RELEASE.jar;D:\oms_resp\org\springframework\spring-aspects\4.3.19.RELEASE\spring-aspects-4.3.19.RELEASE.jar;D:\oms_resp\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar;D:\oms_resp\org\aspectj\aspectjrt\1.9.4\aspectjrt-1.9.4.jar" com.test.main.RubbishTest
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at com.test.main.RubbishTest.main(RubbishTest.java:23)
Process finished with exit code 1
查看GC日志:
2020-10-13T14:50:03.236+0800: 0.135: [GC [PSYoungGen: 76800K->12771K(89600K)] 76800K->51819K(294400K), 0.0411236 secs] [Times: user=0.45 sys=0.01, real=0.04 secs]
2020-10-13T14:50:03.288+0800: 0.187: [GC [PSYoungGen: 89571K->12784K(89600K)] 128619K->99724K(294400K), 0.0566552 secs] [Times: user=0.59 sys=0.03, real=0.06 secs]
2020-10-13T14:50:03.356+0800: 0.256: [GC [PSYoungGen: 89584K->12776K(89600K)] 176524K->176595K(294400K), 0.0863521 secs] [Times: user=0.73 sys=0.00, real=0.09 secs]
2020-10-13T14:50:03.443+0800: 0.342: [Full GC [PSYoungGen: 12776K->0K(89600K)] [ParOldGen: 163819K->152278K(204800K)] 176595K->152278K(294400K) [PSPermGen: 3237K->3236K(21504K)], 1.3309315 secs] [Times: user=5.70 sys=0.05, real=1.33 secs]
2020-10-13T14:50:04.779+0800: 1.678: [GC [PSYoungGen: 28878K->12800K(89600K)] 181157K->181014K(294400K), 0.1707134 secs] [Times: user=1.63 sys=0.00, real=0.17 secs]
2020-10-13T14:50:04.950+0800: 1.849: [Full GC [PSYoungGen: 12800K->0K(89600K)] [ParOldGen: 168214K->180866K(204800K)] 181014K->180866K(294400K) [PSPermGen: 3236K->3236K(21504K)], 1.3125388 secs] [Times: user=6.20 sys=0.08, real=1.31 secs]
2020-10-13T14:50:06.273+0800: 3.171: [Full GC [PSYoungGen: 76800K->16895K(89600K)] [ParOldGen: 180866K->204715K(204800K)] 257666K->221610K(294400K) [PSPermGen: 3236K->3236K(21504K)], 0.7629433 secs] [Times: user=5.23 sys=0.13, real=0.76 secs]
2020-10-13T14:50:07.043+0800: 3.942: [Full GC [PSYoungGen: 66503K->66288K(89600K)] [ParOldGen: 204715K->204715K(204800K)] 271218K->271004K(294400K) [PSPermGen: 3236K->3236K(21504K)], 1.2479725 secs] [Times: user=10.30 sys=0.08, real=1.25 secs]
2020-10-13T14:50:08.291+0800: 5.191: [Full GC [PSYoungGen: 66288K->66288K(89600K)] [ParOldGen: 204715K->204697K(204800K)] 271004K->270986K(294400K) [PSPermGen: 3236K->3236K(21504K)], 0.8842482 secs] [Times: user=6.89 sys=0.11, real=0.89 secs]
Heap
PSYoungGen total 89600K, used 68737K [0x00000000f9c00000, 0x0000000100000000, 0x0000000100000000)
eden space 76800K, 89% used [0x00000000f9c00000,0x00000000fdf20780,0x00000000fe700000)
from space 12800K, 0% used [0x00000000ff380000,0x00000000ff380000,0x0000000100000000)
to space 12800K, 0% used [0x00000000fe700000,0x00000000fe700000,0x00000000ff380000)
ParOldGen total 204800K, used 204697K [0x00000000ed400000, 0x00000000f9c00000, 0x00000000f9c00000)
object space 204800K, 99% used [0x00000000ed400000,0x00000000f9be6680,0x00000000f9c00000)
PSPermGen total 21504K, used 3267K [0x00000000e8200000, 0x00000000e9700000, 0x00000000ed400000)
object space 21504K, 15% used [0x00000000e8200000,0x00000000e8530f80,0x00000000e9700000)
计算结果还是跟上面一样:
堆占用空间为:270986/294400=92%,剩余8%,小于设定的25,抛出OOM;
------------------------------------------------分割线-------------------------------------------------------
有了垃圾回收,java会不会内存泄露
我们来看一段代码:
package com.test.main;
import java.util.ArrayList;
import java.util.List;
public class RubbishTest {
static class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
List<A> list = new ArrayList<A>();
for (int i = 0; i < 3; i++) {
A a = new A();
list.add(a);
a = null;
}
}
}
解释:
这里a指向堆中对象new A()的引用被置空,但是new A()对象还存在一个引用,这个引用就是list指向堆中的一个数组数组对象,数组对象是一片连续的内存空间,每个空间片存储着指向new A()的内存地址。
所以这个new A()不会被垃圾回收器回收,但是这个对象在main栈帧空间被释放,list变量回收,list指向的对象被回收之后,内存当中的new A()就属于内存泄露。
这时候的Gc Roots:a和list都被释放,没有一条路径可以找到new A()对象,那么这个new A()一定会被GC回收吗?不一定,如果调用了finalize()方法可以重新建议Gc Roots连接,只有一次的机会,第二次GC直接忽略,回收new A()对象。
以上仅是个人理解和记忆使用。