热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVMGC垃圾回收

目录1JVM内存结构1.1JVM体系概述1.2四种类装载器1.3双亲委派1.4java类加载过程中的杀箱安全机制1.5Java8以后的JVM2GC作用域3常见的垃圾回

目录

1 JVM内存结构

1.1 JVM体系概述

1.2 四种类装载器

1.3 双亲委派

1.4 java类加载过程中的杀箱安全机制

1.5 Java8以后的JVM

2 GC作用域

3 常见的垃圾回收算法

3.1 引用计数

3.1.1 应用:

3.1.2 缺点:

3.1.3 为什么主流的JAVA虚拟机里面都没有选用这种算法?

3.2 复制(一般使用于年轻代)

3.2.1 MinorGC过程(复制->清空->互换)

3.2.2 特点

3.3 标记清除

3.3.1 垃圾收集算法-标记清除法(Mark-Sweep)图示

3.3.2 缺点

3.4 标记整理

3.4.1 标记-压缩(Mark-Compact)  原理图示

3.4.2 缺点

4 小总结

5 什么是GC Roots

5.1 什么是垃圾

5.2 要进行垃圾回收,如何判断一个对象是否可以被回收,确定是垃圾

5.2.1 引用计数法

5.2.2 枚举根节点做可达性分析(根搜索路径)

5.3 JAVA中哪些可以作为GC Roots 对象?

6 JVM参数配置

6.1 JVM的参数类型

6.1.1 标配参数

6.1.2 X参数

6.1.3 XX参数

7 查看JVM默认值(查看参数盘点家底)

7.1 方案一

7.2 方案二

7.2.1 查看初始默认: -XX:+PrintFlagsInitial

7.2.2 查看修改更新: -XX:+PrintFlagsFinal

7.2.3 PrintFlagFinal举例,运行JAVA命令的同时打印参数

7.2.4 XX:+PrintCommandLineFlags

8 JVM常用基本配置参数有哪些

8.1 -Xmx -Xms

8.1.1 DEMO

8.2 常用参数

8.2.1 -Xms 初始大小内存,默认物理内存的1/64

8.2.2 -Xmx 最大分配内存,默认为物理内存的1/4

8.2.3 -Xss 栈空间

8.2.4 -Xmn 设置新生区,年轻代的大小,一般默认就行

​8.2.5 -XX:MetaspaceSize 设置元空间的大小

8.2.6 典型设置案例

8.2.7 -XX:+PrintGCDetails 输出详细GC收集日志信息 (GC, FullGC)

8.2.8 -XX:SurvivorRatio 幸存区 设置新生代中Eden和S0/S1空间的比例

8.2.9 -XX:NewRatio 配置年轻代与老年代在堆结构的占比

8.2.10 -XX:MaxTenuringThreshold 吞吐量 设置垃圾最大年龄 ​

9 其他(有空再看)

10 参考文献




1 JVM内存结构


1.1 JVM体系概述


1.2 四种类装载器


  1. 启动类(根)加载器  bootstrap
  2. 拓展类加载器
  3. 应用加载器
  4. 自定义:继承class抽象类

1.3 双亲委派


1.4 java类加载过程中的杀箱安全机制

灰色:线程私有

亮色:线程公有


1.5 Java8以后的JVM

堆内存:伊甸园区,幸存者0区,幸存者1区,养老区

java7以前: 永久代

java8 : metaspace 云空间


2 GC作用域


3 常见的垃圾回收算法


3.1 引用计数

Java中,引用和对象是有管理的。如果操作对象 则必须用引用进行。

因此,可通过引用计数来判断一个对象是否可以回收。简单说,给对象加一个引用计数器。

每当有一个地方引用它,计数器值+1;

每当有一个引用失败时,计数器值-1;

任何时刻计数器值为0的对象就是不可能再被使用的,那么这个对象就是可回收对象。

即:有对象引用+1, 没对象引用-1,到0回收


3.1.1 应用:

微软的COM/ActionScrip3/Python...


3.1.2 缺点:


  • 每次对对象赋值时均要飞虎引用计数器,且计数器本身也有一定的消耗。
  • 较难处理循环引用

3.1.3 为什么主流的JAVA虚拟机里面都没有选用这种算法?

JVM的实现一般不采用这种方式,很难解决对象之间互相循环引用的问题


3.2 复制(一般使用于年轻代)

复制之后又交换,谁空谁是two


3.2.1 MinorGC过程(复制->清空->互换)


3.2.2 特点

没内存碎片,浪费空间,大对象费时


3.3 标记清除

算法分成标记和清除两个阶段,先标记出要回收的对象,然后统一回收这些对象

即:先标记,后清除


3.3.1 垃圾收集算法-标记清除法(Mark-Sweep)图示


3.3.2 缺点

存在碎片


3.4 标记整理

标记清除整理


3.4.1 标记-压缩(Mark-Compact)  原理图示


3.4.2 缺点

移动对象需要成本


4 小总结

新生代使用 复制算法(费空间)

老年代使用 标记清除(有内存碎片),标记整理(耗时)



以上是回顾




5 什么是GC Roots


5.1 什么是垃圾

Person person = null; 没人用了,就会被回收


5.2 要进行垃圾回收,如何判断一个对象是否可以被回收,确定是垃圾


5.2.1 引用计数法

可查看前情回顾中 3.1 引用计数的内容


5.2.2 枚举根节点做可达性分析(根搜索路径)

为了解决引用计数器的循环引用问题,JAVA使用了可达性分析的方法:

跟踪(Tracing)


  • 复制(Copying)
  • 标记-清除(Mark-Sweep)
  • 标记-压缩(Mark-Compact)
  • Mark-Sweep(-Compact)

所谓“GC roots”,或者说tracing GC的 “根集合”就是一组必须活跃的引用

 

基本思路就是通过一系列名为“GC Roots”的对象作为起始点,从这个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,说明此对象不可用。

即给定一个集合的引用作为根出发,通过引用关系便利对象图,能够被遍历到的(可到达的)对象就被判定为存活,没有被遍历到的就自然被判定为死亡。

案例:

右侧数据不是从GCROOT开始的,引用不可达,就要被回收。


5.3 JAVA中哪些可以作为GC Roots 对象?


  1. 虚拟机栈(帧栈中的局部变量区,也叫做局部变量表)中引用的对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI(Native方法)引用的对象

6 JVM参数配置

-Xms :初始堆空间

-Xmx :初始堆空间最大值

-Xss  :初始栈空间

.....

from...to...   :从初始值调到期望值

MateSpace :元空间

-Xms和-Xmx,最好调试成一样的,避免忽高忽低


6.1 JVM的参数类型


6.1.1 标配参数

JDK1.0 到 java12一直在


  • -version
  • -help
  • java -showversion

案例:



6.1.2 X参数


  • -Xint:解释执行
  • -Xcomp:第一次使用就编译成本地代码
  • -Xmixed:混合模式

混合模式:javac 先编译class文件 再java 执行


6.1.3 XX参数

6.1.3.1 Boolean类型

公式:

开启(+代表启动,激活true): -XX:+属性值

关闭(-代表本次运行,在这个参数的影响下,没有被激活使用): -XX:-属性值

XX第一个参数是boolean类型,开关

案例:

是否打印GC收集细节:

-XX:-PrintGCDetails

-XX:+PrintGCDetails

是否使用串行垃圾回收器

-XX:-UseSerialGC

-XX:+UseSerialGC

实际操作(如何查看一个正在运行中的java程序,某个JVM参数是否开启,具体值是多少?):

jdk安装的/bin目录中,jps 可查看后台进程, jinfo 查看正在运行的java信息

public class HelloGC {public static void main(String[] args) throws Exception{System.out.println("****HelloGC");Thread.sleep(Integer.MAX_VALUE);}
}

可以看出helloGC的进程是10800

查看进程ID命令:jps -l

打印GC收集细节参数:PrintGCDetails

查看某运行的java信息命令:jinfo -flag 进程ID 参数

-XX:固定写死的XX参数,固定写法

-PrintGCDetails中的 “-”符号代表没有加入过这个参数

增加参数 -XX:+PrintGCDetails,再次测试

可以看出 -XX:+PrintGCDetails, 代表添加了这个参数激活并使用了

6.1.3.2 KV设值类型

公式:

-XX:属性key=属性值value

案例:

-XX:MetaspaceSize=128m

-XX:MaxTenuringThreshold=128m

实际操作:

1.设置元空间

元空间数据查询,没有配置的时候,打印出默认值。21M多

可发现这个数据,没有配置也会出现

现在我们设置一下

2.young区 15年龄 升级到老年区,

6.1.3.3 jinfo案例,如何查看当前运行程序的配置

JVM调优需要了解一下

配置参数 测试:

jinfo -flags 16984 批量查询

Non-default VM flags 初始化加载默认的数据

Command line: 人工修改的

6.1.3.4 -Xmx和-Xmx参数怎么理解

就是简写(因为经常用), 还是XX参数

-Xms :等价于-XX:InitialHeapSize

-Xmx :等价于-XX:MaxHeapSize 

6.1.3.5 小总结

第一个蓝圈,boolean类, 第二个蓝圈,kv型


7 查看JVM默认值(查看参数盘点家底)


7.1 方案一


  1. jps -l
  2. jinfo -flag 具体参数 java进程编号
  3. jinfo -flags java进程编号

7.2 方案二


7.2.1 查看初始默认: -XX:+PrintFlagsInitial

公式:

java -XX:+PrintFlagsInitial -version

java -XX:+PrintFlagsInitial

案例:

如何查看JVM初始参数(所有)

java -XX:+PrintFlagsInitial

global flags java本身的 出厂默认, 没动过的

= 说明是JVM默认加载的

:=  说明JVM默认加载的时候修改的或人为修改过的参数


7.2.2 查看修改更新: -XX:+PrintFlagsFinal

公式:

java -XX:+PrintFlagsFinal -version 


7.2.3 PrintFlagFinal举例,运行JAVA命令的同时打印参数

执行命令: java -XX:+PrintFlagsFinal -version

用修改后的值测试

java -XX:+PrintFlagsFinal -XX:MetaspaceSize=512m T

验证:536870912/1024/1024 = 512


7.2.4 XX:+PrintCommandLineFlags

 -XX:+PrintCommandLineFlags -version

看出了默认的垃圾回收器: -XX:+UseParrallelGC


8 JVM常用基本配置参数有哪些


8.1 -Xmx -Xms

-Xmx -Xms 这些是必须的,基本会配置一样的

以 8处理器CPU,16G内存为例

一般初始的堆内存大小64分之一

一般初始的最大内存量四分之一

243.5M*64=15.584 大约16G


8.1.1 DEMO

 


8.2 常用参数


8.2.1 -Xms 初始大小内存,默认物理内存的1/64

等价于-XX:InitialHeapSize


8.2.2 -Xmx 最大分配内存,默认为物理内存的1/4

等价于-XX:MaxHeapSize 


8.2.3 -Xss 栈空间

栈空间的初始大小 ,私有的 管运行

设置单个线程栈的大小,一般默认为512k~1024k

等价于 -XX:ThreadStackSize

案例

0代表使用的是默认初始值。

尝试重新配置数据 

JAVA8 看官网:https://docs.oracle.com/javase/9/docs/index.html

这个值依赖于平台,windows 默认值依赖于虚拟内存是多大

linux 64位 一般是1024KB


8.2.4 -Xmn 设置新生区,年轻代的大小,一般默认就行


8.2.5 -XX:MetaspaceSize 设置元空间的大小

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。

不过元空间与永久代之间最大的区别在于:


  • 元空间并不在虚拟机中,而是使用本地内存。
  • 因此,默认情况下,元空间的大小仅受本地内存限制。

-Xms10m -Xmx10m +XX:MetaspaceSize=1024m -XX:+PrintFlagsFinal

metaSpaceSize也不是越大越好

OOM里存在这种报错: java.lang.OutOfMemoryError:Metaspace

频繁反复的NEW对象,可能会撑爆元空间MetaspaceSize,所以会调节大一些。默认选择是20M左右


8.2.6 典型设置案例

java -XX:+PrintFlagsFinal -version

JVM调整

-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC


  • -Xms128m : 初始内存128
  • -Xmx4096m : 最大堆内存4G
  • -Xss1024k :初始栈大小1G
  • -XX:MetaspaceSize=512m:元空间512M
  • -XX:+PrintCommandLineFlags:后面的参数
  • -XX:+PrintGCDetails:打印GC回收细节
  • -XX:+UseSerialGC:串行垃圾回收期

实践:

运行demo:

public class HelloGC {public static void main(String[] args) throws Exception{System.out.println("****helloGC");Thread.sleep(Integer.MAX_VALUE);}
}

输出

-XX:InitialHeapSize=265971520   16G内存的64分支一

-XX:MaxHeapSize=4255544320 最大堆内存16G的4分之一

-XX:+PrintCommandLineFlags

-XX:+UseCompressedClassPointers

-XX:+UseCompressedOops

-XX:-UseLargePagesIndividualAllocation

-XX:+UseParallelGC 


****helloGC

以上打印出了JVM默认出厂的内容

-XX:+UseParallelGC 代表并行垃圾回收器

-XX:+UseSerialGC 代表串行垃圾回收器

使用之前的配置

-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC

打印结果: 

-XX:InitialHeapSize=134217728   //128M

-XX:MaxHeapSize=4294967296   //比以前大

-XX:MetaspaceSize=536870912  //512m

-XX:+PrintCommandLineFlags  

-XX:+PrintGCDetails -XX:ThreadStackSize=1024  //栈空间

-XX:+UseCompressedClassPointers

-XX:+UseCompressedOops

-XX:-UseLargePagesIndividualAllocation

-XX:+UseSerialGC //被改变


8.2.7 -XX:+PrintGCDetails 输出详细GC收集日志信息 (GC, FullGC)

测试 java.lang.OutOfMemoryError: Java heap space

配置 -Xmx10m -Xmx10m -XX:+PrintGCDetails,将内存调小

对比两张图

之前没有GC,现在有了GC

[GC (Allocation Failure) [PSYoungGen: 1791K->504K(2560K)] 1791K->680K(9728K), 0.0014547 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

新生1/3大小

养老2/3大小

->分割了前后数据

[Full GC (Allocation Failure) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 248K->638K(5632K)] 736K->638K(8192K), [Metaspace: 3351K->3351K(1056768K)], 0.0061684 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

Full GC一般是养老区


8.2.8 -XX:SurvivorRatio 幸存区 设置新生代中Eden和S0/S1空间的比例

 

新生代一般是8:1:1

设置新生代中Eden和S0/S1空间的比例

默认:-XX:SurvivorRatio=8,Eden:S0:S1=8:1:1

假如:-XX:SurvivorRatio=4,Eden:S0:S1=4:1:1

SurvivorRatio值就是设置Eden区的比例占多少,S0/S1相同

默认的情况

可以看出比例是8:1:1

修改配置

配置后运行:可以看出数据没变, 还是8:1:1

再次调试改成4

综上,该配置是 伊甸园区和幸存区的比例。


8.2.9 -XX:NewRatio 配置年轻代与老年代在堆结构的占比

一般用默认, 不需要调整。

默认:-XX:NewRatio=2 新生代占1,老年代2,年轻代占整个堆的1/3。

假如:-XX:NewRatio=4 新生代占1,老年代4,年轻代占整个堆的1/5。

NewRatio值就是设置老年代的占比,剩下的1给新生代。

一般15次才会去老年代,年轻代比例小了 GC就会频繁

默认:-Xmx10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2

 修改2, 打印结果和默认一致,说明是2

修改4:-Xmx10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=4


8.2.10 -XX:MaxTenuringThreshold 吞吐量 设置垃圾最大年龄 


先看一下默认数据, 默认是15

交换15次,才会从young到old区

秉着减少full GC的情况调试,增加交换要求,young数据停留时间久了,老年代数据减少了,full GC就少了,是否可以呢?下面测试调高


9 其他(有空再看)


10 参考文献

以上内容均来自于下方视频,博客内容仅作为个人学习笔记记录

【1】Java面试_高频重点面试题 (第一、二、三季)_ 面试 第1、2、3季_柴林燕_周阳_哔哩哔哩_bilibili


推荐阅读
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
author-avatar
干将stambach_611
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有