共计 5343 个字符,预计需要花费 14 分钟才能阅读完成。
JVM 垃圾回收的时候如何确定垃圾?
什么是垃圾?
垃圾:简略来说就是内存中曾经不再被应用到的空间就是垃圾
如何判断一个对象是否能够被回收?
- 援用计数法(该算法曾经过期了,解决不掉循环援用的问题)
每当有一个中央援用它,计数器值加 1;每当有一个援用生效,计数器值减 1
任何时刻计数器值为零的对象就是不可能再被应用的,那么这个对象就是可回收对象。
- 枚举根节点做可达性剖析
基本思路就是通过一系列名为 GC Roots 的对象作为起始点,从这个被称为 GC Roots 的对象开始向下搜寻,如
果一个对象到 GC Roots 没有任何援用链相连,则阐明此对象不可用。
那些对象能够当做 GC Roots?
- 虚拟机栈(栈帧中的局部变量表)中的援用对象
- 办法区中的类动态属性援用的对象
(static)
- 办法区中常量援用的对象(例如字符串常量池里的援用)
- 本地办法栈中的 Native 办法的援用对象
代码阐明
public class GCRootDemo {
// private static GCRootDemo2 t2; // 办法区中的类动态属性援用的对象
// private static final GCRootDemo3 t3 = new GCRootDemo3(8); // 办法区中的常量援用
public static void m1() {GCRootDemo t1 = new GCRootDemo(); // 虚拟机栈中的援用对象
System.gc();
System.out.println("第一次 GC 实现");
}
public static void main(String[] args) {m1();
}
}
如何盘点查看 JVM 零碎默认值?
JVM 的参数类型
标配参数
- -version
- -help
- java -showversion
x 参数(理解)
- -Xint(解释执行)
- -Xcomp(第一次应用就编译成本地代码)
- -Xmixed(混合模式:先编译再执行)
xx 参数
1、xx 参数之布尔类型
公式:-XX:+
或者 -XX:-
,+ 示意开启,- 示意敞开
案例:开启打印 GC 收集细节。-XX:+PrintGCDetails
2、xx 参数之 key-value 类型
公式:-XX: 属性 key= 属性 value
案例:查看 Java 元空间大小默认:-XX:MetaspaceSize=21807104
。
如果想批改 kv 类型的值,把等号前面的值间接改了即可
如何查看运行的 Java 程序,JVM 参数是否开启,具体值为多少?
首先咱们运行一个 HelloGC 的 java 程序,我想查看 PrintGCDetails 参数是否开启了。
public class HelloGC {public static void main(String[] args) throws InterruptedException {System.out.println("hello GC");
Thread.sleep(Integer.MAX_VALUE);
}
}
1、先通过jps -l
,失去过程号。
2、而后应用 jinfo -flag 具体参数 过程号
而后查看是否开启 PrintGCDetails 这个参数
如果咱们想开启这个参数,须要在运行程序的时候配置 JVM 参数。在 IDEA 中能够点Run——Edit Configurations,
而后在 VM Options 中退出上面的代码,当初 + 号示意开启。
-XX:+PrintGCDetails
再次查看咱们的配置,咱们看到原来的 - 号变成了 + 号,阐明咱们通过 VM Options 配置的 JVM 参数曾经失效了!!
jinfo -flags 过程号
。是打印出搜寻到的全副参数,Command line 代表本人配置的。
题外话(坑题)
两个经典参数:-Xms 和 -Xmx,这个如何解释?
- -Xms:等价于
-XX:InitialHeapSize
(初始化堆内存) - -Xmx:等价于
-XX:MaxHeapSize
(最大堆内存)
因为比拟罕用,相当于起了个别名,类型属于 xx 参数。
JVM 的初始参数盘点
-XX:+PrintFlagsInitial
(次要是查看初始默认值)
-XX:+PrintFlagsFinal
(查看批改当前,最终的值)
咱们发现有一些参数有
:=
,示意人为批改过或 jvm 本人批改过的,=
示意没有批改过的
-XX:+PrintCommandLineFlags
这个最不便的是能够间接看到,当初默认的垃圾回收器用的是哪一个。
工作中 JVM 的罕用配置有哪些?
查看堆内存
用 java 程序,查看 JVM 的初始化堆内存 -Xms 和最大堆内存 Xmx
public static void main(String[] args) throws InterruptedException {
// 返回 Java 虚拟机中内存的总量
long totalMemory = Runtime.getRuntime().totalMemory();
// 返回 Java 虚拟机中试图应用的最大内存量
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("TOTAL_MEMORY(-Xms) =" + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
System.out.println("MAX_MEMORY(-Xmx) =" + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
}
打印后果为:
TOTAL_MEMORY(-Xms) = 128974848(字节)、123.0MB
MAX_MEMORY(-Xmx) = 1884815360(字节)、1797.5MB
罕用参数
栈管运行,堆管存储
-
-Xms(-XX:InitialHeapSize)
初始堆大小内存,默认为物理内存的 1 /64
-
-Xmx(-XX:MaxHeapSize)
最大分配内存,默认为物理内存的 1 /4
-
-Xss(-XX:ThreadStackSize)
设置单个线程栈的大小,个别默认为 512k~1024k。
应用 jinfo -flag ThreadStackSize
会发现 -XX:ThreadStackSize = 0
为什么呢?
查看官网发现,0 代表默认值,这个默认值的大小取决于平台。例如 Linux/x64、OS X 是 1024KB,Windows 取决于虚拟内存的大小。
-
-Xmn
设置年老代大小,个别用默认值就行。
-
-XX:MetaspaceSize
设置元空间大小。(元空间的实质和永恒代相似,都是对 JVM 标准中办法区的实现。他们最大的区别在于:元空间并不在虚拟机中,而是应用本地内存,因而元空间的大小仅受本地内存限度)。
默认的大小为 21807104,相当于 21M。为了避免在频繁的实例化对象的时候,让元空间呈现 OOM。要把内存调大些,能够调成 1024m!
-
典型设置案例
-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags
-XX:+PrintGCDetails -XX:+UseParallelGC
-
-XX:PrintGCDetails
- 输入具体 GC 收集日志信息
-
首先咱们用一段代码,制作出垃圾回收的过程
// 首先咱们设置一下程序的启动配置: 设置初始堆内存为 10M,最大堆内存为 10M // 而后用下列代码,创立一个 十分大空间的 byte 类型数组 byte [] byteArray = new byte[50 * 1024 * 1024];
运行后,发现会呈现下列谬误,这就是 OOM:java 内存溢出,也就是堆空间有余
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.moxi.interview.study.GC.HelloGC.main(HelloGC.java:22)
同时打印出了 GC 垃圾回收时候的详情 ……
[GC (Allocation Failure) [PSYoungGen: 1972K->504K(2560K)] 1972K->740K(9728K), 0.0156109 secs] [Times: user=0.00 sys=0.00, real=0.03 secs] [GC (Allocation Failure) [PSYoungGen: 504K->480K(2560K)] 740K->772K(9728K), 0.0007815 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2560K)] [ParOldGen: 292K->648K(7168K)] 772K->648K(9728K), [Metaspace: 3467K->3467K(1056768K)], 0.0080505 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 648K->648K(9728K), 0.0003035 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 648K->630K(7168K)] 648K->630K(9728K), [Metaspace: 3467K->3467K(1056768K)], 0.0058502 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap PSYoungGen total 2560K, used 80K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd143d8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 7168K, used 630K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 8% used [0x00000000ff600000,0x00000000ff69dbd0,0x00000000ffd00000) Metaspace used 3510K, capacity 4500K, committed 4864K, reserved 1056768K class space used 389K, capacity 392K, committed 512K, reserved 1048576K
这 tm 看不懂啊,咱们通过这张图来解析外面的参数:
YoungGC:
Full GC:(大部分产生在养老区)
法则:[名称:GC 前内存占用 -> GC 后内存占用 (该区内存总大小)]
-
-XX:SurvivorRatio
-
java 堆从 GC 的角度能够细分为:新生代 (Eden 区、FromSurvivor 区、ToSurvivor 区)和 老年代。
- 设置 新生代中 eden 和 S0/S1 空间的比例。默认值为:
-XX:SurvivorRatio=8
,即Eden:S0:S1=8:1:1
。 - 如果
-XX:SurvivorRatio=4
,则Eden:S0:S1=4:1:1
。Survivor 值就是设置 Eden 区的比例占多少,S0/S1 是雷同的。
-
-
-XX:NewRatio
- 设置 年老代与老年代在堆中的比例。默认值为:
-XX:NewRatio=2
,即新生代占 1,老年代占 2,年老代占整个堆的 1 /3。 - 如果
-XX:NewRatio=4
,则新生代占 1,老年代占 4,年老代占整个堆的 1 /5。NewRatio 值就是 设置老年代的占比,剩下的 1 给新生代。
如果新生代特地小,会造成频繁的进行 GC 收集
- 设置 年老代与老年代在堆中的比例。默认值为:
-
-XX:MaxTenuringThreshold
- 设置 垃圾最大年龄 ,SurvivorTo 和 SurvivorFrom 调换,原 SurvivorTo 成为下一次 GC 时的 SurvivorFrom 区,局部对象会在 From 和 To 区域中复制来复制去,如此替换 15 次(由 JVM 参数
MaxTenuringThreshold
决定,这个参数默认为 15),最终如果还是存活,就存入老年代。 - 默认是 15,并且设置的值 在 0~15 之间
(因为就四个字节,最大 1111=15)
- 如果设置为 0 的话,则年老对象不通过 Survivor 区,间接进入老年代;对于老年代比拟多的利用,能够提高效率。如果将此值设置较大,则年老代对象会在 Survivor 区进行屡次复制,这样能够减少对象在年老代的存活空间。
- 设置 垃圾最大年龄 ,SurvivorTo 和 SurvivorFrom 调换,原 SurvivorTo 成为下一次 GC 时的 SurvivorFrom 区,局部对象会在 From 和 To 区域中复制来复制去,如此替换 15 次(由 JVM 参数