jvm GC:垃圾回收的测试与分析
实验环境:
(1)Java版本以及模式:
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
(2)垃圾回收器为Parrallel Scanvenge收集器 和 Parrallel Old收集器
(3)CPU:Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
1、查看基础内存消耗
public class GCTest {
public static void main(String[] args){}
} //使用javac编译并执行java -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 GCTest
// 日志如下:
Heap
PSYoungGen total 9216K, used 1312K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 16% used [0x00000000ff600000,0x00000000ff748228,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
Metaspace used 2538K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 278K, capacity 386K, committed 512K, reserved 1048576K
分析日志可知:eden区基础消耗1312K的内存,其中新生代大小为9216K = 8192 + 1024 K,因为 to 区和 from 区只有其中一个能使用。我并不知道基础消耗到哪里去了?有知道的大神告诉我下。
注意:使用cmd命令行执行,基础内存消耗较低,原先使用idea执行基础内存消耗达到2M。
2、新创建的对象内存大于新生代剩余内存将直接分配到老年代
import sun.misc.Unsafe; import java.lang.reflect.Field; public class GCTest {
private static Unsafe unsafe; static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
} public static long addressOf(Object o) throws Exception {
Object[] array = new Object[] { o };
long baseOffset = unsafe.arrayBaseOffset(Object[].class);
int addressSize = unsafe.addressSize();
long objectAddress;
switch (addressSize) {
case 4:
objectAddress = unsafe.getInt(array, baseOffset);
break;
case 8:
objectAddress = unsafe.getLong(array, baseOffset);
break;
default:
throw new Error("unsupported address size: " + addressSize);
}
return (objectAddress);
} private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB];
// 打印对象所在的起始内存地址
System.out.println("allocation1:0x00000000" + Long.toHexString(addressOf(allocation1)));
System.out.println("allocation2:0x00000000" + Long.toHexString(addressOf(allocation2)));
System.out.println("allocation3:0x00000000" + Long.toHexString(addressOf(allocation3)));
System.out.println("allocation4:0x00000000" + Long.toHexString(addressOf(allocation4)));
} } //使用javac编译并执行java -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 GCTest
// 日志如下:
allocation1:0x00000000ff71f158 // 处于新生代
allocation2:0x00000000ff91f168 // 处于新生代
allocation3:0x00000000ffb1f178 // 处于新生代
allocation4:0x00000000fec00000 // 处于老年代
Heap
PSYoungGen total 9216K, used 7456K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 91% used [0x00000000ff600000,0x00000000ffd48258,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
Metaspace used 2568K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 284K, capacity 386K, committed 512K, reserved 1048576K
分析日志可知:Minor GC没有触发,由于新生代剩余大小也放不下allocation4,于是allocation4直接分配到老年代中。
3、Minor GC的触发与过程
import sun.misc.Unsafe; import java.lang.reflect.Field; public class GCTest {
private static Unsafe unsafe; static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
} public static long addressOf(Object o) throws Exception {
Object[] array = new Object[] { o };
long baseOffset = unsafe.arrayBaseOffset(Object[].class);
int addressSize = unsafe.addressSize();
long objectAddress;
switch (addressSize) {
case 4:
objectAddress = unsafe.getInt(array, baseOffset);
break;
case 8:
objectAddress = unsafe.getLong(array, baseOffset);
break;
default:
throw new Error("unsupported address size: " + addressSize);
}
return (objectAddress);
} private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[3 * _1MB]; // 触发Minor GC
allocation4 = new byte[2 * _1MB];
System.out.println("allocation1:0x00000000" + Long.toHexString(addressOf(allocation1)));
System.out.println("allocation2:0x00000000" + Long.toHexString(addressOf(allocation2)));
System.out.println("allocation3:0x00000000" + Long.toHexString(addressOf(allocation3)));
System.out.println("allocation4:0x00000000" + Long.toHexString(addressOf(allocation4)));
} }
// 使用javac编译并执行java -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 GCTest
// 日志如下:
[GC (Allocation Failure) [PSYoungGen: 5244K->768K(9216K)] 5244K->4872K(19456K), 0.0031067 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
allocation1:0x00000000fec00000 // 处于老年代
allocation2:0x00000000fee02010 // 处于老年代
allocation3:0x00000000ff600000 // 处于新生代
allocation4:0x00000000ff900010 // 处于新生代
Heap
PSYoungGen total 9216K, used 6129K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 65% used [0x00000000ff600000,0x00000000ffb3c410,0x00000000ffe00000)
from space 1024K, 75% used [0x00000000ffe00000,0x00000000ffec0030,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002020,0x00000000ff600000)
Metaspace used 2569K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 284K, capacity 386K, committed 512K, reserved 1048576K
分析日志:
(1)进行了一次Minor GC,其中新生代占用大小从5244K 减少为 768K,总占用从5244K 减少为 4872K,清理掉的内存大小 5244 - 4872 = 372K,则老年代大小为 5244 - 768 - 372 = 4104K,该值与最终的老年代大小占用相同。新生代的768K为from space持有,因为1024 * 75% = 768K.
(2) Minor GC的触发条件:当Eden区域放不下新创建的对象,但是新生代的剩余空间大于新创建的对象大小时。
放入allocation1和allocation2时: 1314K(基础消耗)+ 2 * 1024K(allocation1)+ 2*1024K(allocation2)= 5410K 小于Eden区
放入allocation3时: 5410K + 3072K = 8482K 大于Eden区域(8192K) 小于新生代大小(9216K) 此时触发Minor GC
Minor GC的过程:
(1)将存活对象从Eden区将对象转入from区域(如:上述from space的内存占用为768K)。
(2)转移期间,对象太大,将直接进入老年代(如:上述老年代的4104K的内存大小)。
(3)转移后,非存活对象将被清理掉(如:上述372K的内存大小)。
4、Full GC触发与过程
import sun.misc.Unsafe; import java.lang.reflect.Field; public class GCTest {
private static Unsafe unsafe; static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
} public static long addressOf(Object o) throws Exception {
Object[] array = new Object[] { o };
long baseOffset = unsafe.arrayBaseOffset(Object[].class);
int addressSize = unsafe.addressSize();
long objectAddress;
switch (addressSize) {
case 4:
objectAddress = unsafe.getInt(array, baseOffset);
break;
case 8:
objectAddress = unsafe.getLong(array, baseOffset);
break;
default:
throw new Error("unsupported address size: " + addressSize);
}
return (objectAddress);
} private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[3 * _1MB]; // 转入老年代时,由于allocation2 大于 allocation1,所以触发了Full GC
allocation3 = new byte[2 * _1MB]; // 触发Minor GC
System.out.println("allocation1:0x00000000" + Long.toHexString(addressOf(allocation1)));
System.out.println("allocation2:0x00000000" + Long.toHexString(addressOf(allocation2)));
System.out.println("allocation3:0x00000000" + Long.toHexString(addressOf(allocation3)));
}
}
// 使用javac编译并执行java -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 GCTest
日志如下:
[GC (Allocation Failure) [PSYoungGen: 6268K->768K(9216K)] 6268K->5896K(19456K), 0.0036739 secs] [Times: user=0.01 sys=0.05, real=0.02 secs]
[Full GC (Ergonomics) [PSYoungGen: 768K->0K(9216K)] [ParOldGen: 5128K->5750K(10240K)] 5896K->5750K(19456K), [Metaspace: 2559K->2559K(1056768K)],
0.0068489 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
allocation1:0x00000000fec00000 // 处于老年代
allocation2:0x00000000fee004e8 // 处于老年代
allocation3:0x00000000ff600000 // 处于新生代
Heap
PSYoungGen total 9216K, used 2289K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 27% used [0x00000000ff600000,0x00000000ff83c770,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 5750K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 56% used [0x00000000fec00000,0x00000000ff19d8e8,0x00000000ff600000)
Metaspace used 2569K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 284K, capacity 386K, committed 512K, reserved 1048576K
分析日志:
(1)发生一次Minor GC与一次Full GC,第一次Minor GC回收了 6268K - 5896K = 372K 内存,新生代剩余768K内存,这一步和上一个例子相同。
(2)由于allocation1和allocation2的大小大于from space区,所以需要直接转入老年代。但由于allocation2大于allocation1,触发Full GC来防止老年代的空间不足以存放新的对象。
这次GC回收了5896K - 5750K = 146K的内存空间,新生代内存占用变为0K,老年代内存占用添加768K - 146K = 622K。老年代内存占用= 622K+allocation1(2*1024)+allocation2(3*1024) = 5742K (约等于5750K)
多出来的8K,我也不知道在哪里了,有大神知道请告诉我。
(3)allocation3直接放入了Eden区域,新生代最终内存占用为 2048K(约等于2289K)。
Full GC的过程:
(1)一般会触发一次Minor GC,即from space区对象的存活时间较长能够升入老年代。
(2)老年代垃圾回收,将非存活对象回收。
5、总结
(1)Minor GC触发条件:新创建的对象大于Eden区剩余空间,小于新生代剩余空间时触发;Full GC时伴随触发一次。
(2)Full GC触发条件:由新生代转入老年代时,对象内存占用大于平均转入老年代的对象内存占用。
jvm GC:垃圾回收的测试与分析的更多相关文章
- Java学习之二(线程(了解) JVM GC 垃圾回收)
线程与进程(了解)→JVM→字节码→GC 一.程序 = 算法 + 数据结构(大佬) 二.程序 = 框架 + 业务逻辑(现实) 1.线程与进程.同步与异步 1.1进程是什么? 进程就是操作系统控制的基本 ...
- Java 中级 学习笔记 2 JVM GC 垃圾回收与算法
前言 在上一节的学习中,已经了解到了关于JVM 内存相关的内容,比如JVM 内存的划分,以及JDK8当中对于元空间的定义,最后就是字符串常量池等基本概念以及容易混淆的内容,我们都已经做过一次总结了.不 ...
- JVM专题3: GC 垃圾回收
合集目录 JVM专题3: GC 垃圾回收 什么是GC? 为什么要有 GC? Garbage Collection, 用于内存回收. 简述一下 Java 垃圾回收机制? 那些内存需要回收 虚拟机中程序计 ...
- JVM学习02:GC垃圾回收和内存分配
JVM学习02:GC垃圾回收和内存分配 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. GC垃圾回收和内存分配知识要点Xmind梳理 案例分析1-(G ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- JVM架构和GC垃圾回收机制详解
JVM架构图分析 下图:参考网络+书籍,如有侵权请见谅 (想了解Hadoop内存溢出请看:Hadoop内存溢出(OOM)分类.参数调优化) JVM被分为三个主要的子系统 (1)类加载器子系统(2)运行 ...
- Java 虚拟机 - GC 垃圾回收机制分析
Java 垃圾回收(Garbage Collection,GC) Java支持内存动态分配.垃圾自动回收,而 C++ 不支持.我想这可能也是 为什么 Java 脱胎于 C++ 的一个原因吧. GC 的 ...
- JVM中垃圾回收机制如何判断是否死亡?详解引用计数法和可达性分析 !
因为热爱,所以坚持. 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们 ...
- JVM学习——垃圾回收GC(学习过程)
JVM学习-垃圾回收(GC) 2020年02月19日06:03:56,开始学习垃圾回收,学习资料来源(张龙老师的JVM课程) JVM内存数据区域知识复习 学习垃圾回收之前,要对JVM内部的内存区域有详 ...
随机推荐
- Java后端开发规范
基于阿里巴巴JAVA开发规范整理 一.命名风格 [强制]类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO 正例:Marco ...
- Explain详解
explain语句用于查看某个查询语句具体使用了什么执行计划 执行输出各列详解 table 每条记录对应一个表的查询,如果是两表连接查询,就会有两条记录,table对应查询表名 id 查询语句可能是单 ...
- spark MLlib的 pipeline方式
spark mllib的pipeline,是指将多个机器学习的算法串联到一个工作链中,依次执行各种算法. 在Pipeline中的每个算法被称为"PipelineStage",表示其 ...
- ffmpeg编码h264设置规格
ffmpeg -i demo.ts -profile:v baseline -vcodec h264 -acodec aac -f flv demo.flv
- Kafka 概述
Kafka 是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域. Kafka 中,客户端和服务器之间的通信是通过 TCP 协议完成的. 一.传统消息 ...
- 数据结构之栈(stack)
1,栈的定义 栈:先进后出的数据结构,如下图所示,先进去的数据在底部,最后取出,后进去的数据在顶部,最先被取出. 栈常用操作: s=Stack() 创建栈 s.push(item) 将数据item放在 ...
- nginx详解(代理服务器的解释+nginx 在linux 下的安装+nginx.conf 中的配置解释)
一.概论 1.什么是代理服务器 代理服务器,客户机在发送请求时,不会直接发送给目的主机,而是先发送给代理服务器,代理服务接受客户机请求之后,再向主机发出,并接收目的主机返回的数据,存放在代理服务器的硬 ...
- <JavaScript>如何阅读《JavaScript高级程序设计》(一)
题外话 最近在看<JavaScript高级程序设计>这本书,面对着700多页的厚书籍,心里有点压力,所以我决定梳理一下..探究一下到底怎么读这本书.本书的内容好像只有到ES5...所以只能 ...
- Java:基础知识点
1. 面向对象的特征 (1)封装:把过程和数据包围起来,对数据的访问只能通过已定义的界面.即现实世界可以被描绘成一系列完全自治.封装的对象,这些对象通过一个受保护的接口访问其他对象:(2)继承:是一种 ...
- Kotlin中单例Singleton模式
package loaderman.bar class Singlenton private constructor(){ public var value:Singlenton?=null priv ...