JVM内功心法

基础

  • JVM运行时内存消耗包括哪些部分

通常包含如下几部分:

  1. Heap:我们常说的JVM运行时堆内存(通过-Xmx, -Xms参数控制)
  2. Thread Stack:Java的每一个线程最多会占用一个Thread Stack大小的内存(通过-Xss参数控制)
  3. Direct Memory:一些网络编程框架常会使用,如,Netty(通过-XX:MaxDirectMemorySize参数控制)
  4. JNI Native Memory:一些通过JNI实现的库会使用,如,压缩用到的Deflater
  5. Meta Space(JDK8)/ Perm Gen(JDK7)(通过-XX:MaxPermSize、-XX:MaxMetaspaceSize参数控制)
  6. Code Cache:用于编译和保存本地代码(native code)的内存(通过-XX:ReservedCodeCacheSize参数控制)
  7. JVM自身使用的内存:存储一些JVM内部的数据结构(如,GC用到的数据结构)

 

  • 分代内存管理

Hotspot虚拟机的heap内存被划分成Young区和Old区。

  • Young区:包含一个Eden和两个Survivor区,当此区域用完时会发生Young GC(或者叫做Minor GC)。绝大部分对象都在Eden区分配,至少经过一次Young区垃圾回收后依然存活的对象会被移到Survivor区。两个Survivor区同一时间只有一个包含了对象(叫做From区),另外一个为空(叫做To区)。每一次Young GC会把Eden区以及From区存活的对象Copy到To区,GC结束后From区和To区交换,原来的From区变成To区(此时为空),原来的To区叫做From区(此时包含了GC后Eden区及原From区依然存活的对象)。
  • Old区:在Survivor区存活超过一定次数的对象会被移到Old区,也有一部分大对象直接在Old区进行分配。当此区域用完时会发生Full GC(或者叫做Major GC),Full GC发生时会对Young区和Old区都进行垃圾回收

 

  • Stop the World(STW)

进程内的所有应用线程停止运行,如果发生STW的时候一个线程正在处理请求(如,一个远程调用请求),则此请求只有STW结束后才会继续执行,所以此请求的处理时间至少是STW的时间。

 

  • Concurrent 和 STW 的GC对比

    • Concurrent GC在某些GC阶段还是需要STW的,详见下面CMS的介绍
    • 由于Concurrent GC执行时,GC线程和应用线程同时运行,所以需要关注两个问题:1. CPU使用,GC线程会消耗CPU导致系统CPU变高,当系统CPU不足时可能会导致应用线程的CPU时间片被GC线程抢占而导致处理变慢;2. 因为GC发生时应用线程仍在运行,同时也会产生内存分配,所以需要提前进行Concurrent GC,保证heap内可用内存足以支撑到GC完成。否则,Concurrent GC会失败,然后重新进入STW的GC

 

  • Young GC(Minor GC)

    • 所有的Young GC都会STW
    • 存活对象会被Copy到Survivor区,GC后的Eden区和另外一个Survivor区只包含不再被引用的对象,这两块区域可以被完全重新使用而不需要进行内存碎片整理。所以Young GC的耗时只和Young区存活的对象数量和大小有关,和Young区大小无关
    • 在Survivor区存活时间超过一定次数(-XX:MaxTenuredThreshold)的对象将会移动到Old区
    • 如果存活对象大于一个Survivor区,则全部移动到Old区

 

  • Full GC(Major GC)

    • 对整个Heap进行回收,不仅限于Old区
    • 耗时和整个Heap大小、存活对象数量,对象之间引用关系有关

 

  • 常用GC

    • Young区
      • 串行Copy收集器(-XX:+UseSerialGC):单线程,STW,Old区可以使用串行收集器或者CMS收集器
      • PS Scavenge(-XX:+UseParallelGC):并行,STW,Old区可以使用串行收集器或者PS MarkSweep收集器
      • ParNew(-XX:+UseParNewGC):并行,STW,Old区可以使用串行收集器或者CMS收集器
      • G1(-XX:+UseG1GC):并行,STW,Old区也使用G1
    • Old区
      • 串行MarkSweepCompact收集器(-XX:+UseSerialGC):单线程,STW
      • PS MarkSweep收集器(-XX:+UseParallelOldGC,设置了这个后自动会设置-XX:+UseParallelGC):并行,STW
      • Concurrent Mark-Sweep(CMS, -XX:+UseConcMarkSweepGC):部分阶段并行(STW),部分阶段并发
      • G1(-XX:+UseG1GC):部分阶段并行(STW),部分阶段并发

 

  • 并行收集器

    • 整个回收过程STW
    • 由于整个过程STW,不需要担心回收过程中产生新的对象,且CPU资源可以被GC线程充分利用,所以回收的效率和吞吐都比较高,适合于后台运算而不需要太多交互(对响应时间要求不高)的应用(如后台批处理任务,异步消息消费者等)
  • Concurrent Mark-Sweep(CMS:标记清除算法) 收集器

    • 由于是Concurrent的,所以适合对响应时间要求高,且CPU资源比较充裕的应用

    • 需要考虑的点请参看上面关于“Concurrent和STW的GC对比”

    • 当剩余内存不足以完成Concurrent回收时,退化成单线程的Full GC
    • 不一定会进行内存整理,所以需要考虑内存碎片问题
    • 整个过程中依然存在STW阶段,Remark阶段STW且多线程并行,见下图
  • G1收集器

    • 设计初衷用于替换CMS收集器,适合大堆(>=6G)
    • 和CMS收集器类似,可以并发执行,但是G1会进行内存整理
    • STW时间可控
    • 当发生Full GC时单线程执行

 

  • 参考资料

Java Garbage Collection Basics

Memory Management in the Java HotSpot™ Virtual Machine

Java Performance - The Definitive Guide

Getting Started with the G1 Garbage Collector

常用JVM参数

参数

描述

-Xms

初始堆大小
-Xmx最大堆大小
-XmnYoung区大小(初始和最大值均为此值)
-Xss每个线程的Stack大小
-XX:PermSize=size初始Perm Gen大小
-XX:MaxPermSize=size最大Perm Gen大小
-XX:NewSize=size初始Young区大小
-XX:MaxNewSize=size最大Young区大小
-XX:MetaspaceSize=size当meta space大小超过此值时,会触发一次GC
-XX:MaxMetaspaceSize=size最大meta space大小

-XX:ReservedCodeCacheSize=size

最大Code Cache大小

-XX:+HeapDumpOnOutOfMemoryError

当发生OOM时,产生heap dump

-XX:HeapDumpPath=path

设置当参数-XX:+HeapDumpOnOutOfMemoryError打开时heap dump文件存放的路径和文件名
-XX:NewRatio=ratio设置young区和old区的比例
-XX:+PrintGC每次GC发生时打印GC信息
-XX:+PrintGCTimeStamps每次GC发生时打印时间戳
-XX:+PrintGCDetails每次GC发生时打印GC详细信息
-XX:SurvivorRatio=ratio设置eden区和survivor区的比例


常用JVM工具

  • jps

用来查看基于HotSpot的JVM里面中,所有具有访问权限的Java进程的具体状态, 包括进程ID,进程启动的路径及启动参数等等,与unix上的ps类似,但是jps仅显示Java进程。

  • jstat

用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计。

常用模式

  • jstat -gc {进程号}:垃圾回收堆的行为统计
  • jstat -gcold {进程号}:统计老年代行为
  • jstat -gcnew {进程号}:统计新生代行为
  • jstat -gccause {进程号}:垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
  • jstat -gcutil {进程号}:同-gc,输出的是已使用空间占总空间的百分比
  • jstat -gccapacity {进程号}:同-gc,还会输出Java堆各区域使用到的最大、最小空间
  • jmap

打印出某个java进程(使用pid)内存中所有对象的情况(如:产生那些对象,及其数量)。

可以用于产生heap dump

常用模式

  • jmap -dump:live,format=b,file={文件路径和文件名} {进程号}:dump heap到文件,format指定输出格式,live指明是仅包含存活的对象(注意:加上此参数会触发一次Full GC)
  • jmap -permstat {进程号}:打印Java堆内存的永久区的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
  • jmap -histo {进程号}:打印堆的对象统计,包括对象数、内存大小等等。jmap -histo:live 这个命令执行,JVM会先触发gc,然后再统计信息
  • jmap -heap {进程号}:打印heap的概要信息,GC使用的算法,heap的配置及使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况
  • jstack

jstack用于打印出给定的java进程ID的Java堆栈信息

常用模式

  • jstack {进程号}:打印此Java进程此时的所有运行时线程的堆栈信息

 

用于分析heap dump文件,可以用来排查内存泄漏问题、也可以用来对java进程的内存使用情况进行分析

用于收集Java进程运行时的诊断信息,内存、cpu使用情况以及各种JVM内部和系统级别的事件,以便对此Java进程进行Profiling。场景举例如下:

  1. 查看运行时哪些类分配内存最多,方便进行内存使用调优
  2. 查看运行时哪些执行路径占用最多CPU,方便进行CPU调优
  3. 查看上下文切换情况
  4. 查看IO情况
  5. ...

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值