JVM 那些事 开发二部  陆松林 © All rights reserved
contents (一)  java 内存的管理与调配 (二)  JVM 与线程 (三)常见的 OOM (四)调优实例 (五)思考总结
JVM 都有哪些? 1.Sun HotSpot 2.BEA Jrockit 3.IBM J9 4.apache Harmony 5.Dalvik … ..
JVM 标准结构 Class loader Execution engine Runtime data area Native interface
JVM 标准结构
(一) java 内存的管理与调配 内存结构图
(一) java 内存的管理与调配 方法区: 包括 :  类型基本信息,常量池,字段信息,方法信息,类变量,指向 ClassLoader 的引用, Class 类的引用,方法表等 java 堆: 用来存入由 new() 方式创建的对象和数组。储该对象指向方法区中类型信息的指针。 java 栈: 当 JVM 创建一个新线程时,都会产生线程计数器( PC Register )和栈。每一次方法调用都会产生栈帧,栈帧中包含局部变量区和操作数栈 。 PC 寄存器(线程计数器): 每个线程拥有自己的程序计数器,它指向下一条指令。 本地方法栈: 线程调用时 , 可接口访问运行时的数据区,直接调用本地处理器的寄存器和取任意内存。
(一) java 内存的管理与调配
(一) java 内存的管理与调配 内存申请 第 1 步 .  从新生代申请一块内存 第 2 步 .  新生代不够 第 3 步 .  释放不活跃的对象( minor collection ),若不够,部分活跃对象放入幸存区 第 4 步 .  旧生区空间足够时,幸存区的对象会被移到旧生区区 第 5 步 .  旧生区空间不够时, major collection 第 6 步 .  完全垃圾收集后,旧生区和幸存区无法存放对象,新生区无法创建新内存区 , 出现 OOM
(一) java 内存的管理与调配 内存回收 常用收集器 : 1. 引用计数收集器  2. 跟踪收集器 1.  串行 GC ( Serial Copying ) : 强引用,软引用,弱引用,虚引用。  2. 并行回收 GC(Parallel Scavenge): 多线程  3. 并行 GC(ParNew) 旧生代和持久代可用的 GC: 串行 GC(Serial MSC) : 单线程方式,耗时长 .  并行 GC(Parallel MSC) :多线程,停时间缩短。  并发 (CMS):  GC 的动作和应用的动作并发,分配内存的动作和回收内存的动作同时进行。 新生可用的 GC:
(一) java 内存的管理与调配 常用参数说明 参数 说明 默认值 Xms/-Xmx 定义堆内存的启动值和最大值,建议在生产环境下将两值设为相同 默认是物理内存的 1/64 但小于 1G -Xmn 设置新生代的内存大小 Sun 官方推荐配置为整个堆的 3/8 -XX:PermSize/-XX:MaxPermSize 定义 Perm 段的启动值和最大值,建议在生产环境下将两值设为相同 -XX:NewRaito 设置 YOUNG 与 OLD 段的比值 1/8 -Xss 设置栈的大小 JDK5.0 以后每个线程堆栈大小为 1M ,以前每个线程堆栈大小为 256K … .
(一) java 内存的管理与调配 各代大小调配 新生代大小不能设置过小 新生代大小不能设置过大 避免 Survivor 区过小或过大 合理调配新生代的存活周期
(一) java 内存的管理与调配 常用的命令 命令  参数及说明 jmap ( linux 下特有,也是很常用的一个命令) 如: jmap -histo 2081  |  jmap -histo:live 2081 -heap :打印 jvm heap 的情况 -histo :打印 jvm heap 的直方图。其输出信息包括类名,对象数量,对象占用大小。 -histo : live  :同上,但是只答应存活对象的情况 -permstat :打印 permanent generation heap 情况 jstat (可以观察到 classloader , compiler , gc 相关信息) 如: jsat –gcutil  2081  1000 10  (每隔 1 秒监控一次,一共做 10 次) -gcutil :统计 gc 时, heap 情况 -class :统计 class loader 行为信息 -gc :统计 jdk gc 时 heap 信息 -compile :统计编译行为信息 -gccapacity :统计不同的 generations( 包括新生区,老年区, permanent 区)相应的 heap 容量情况 -gccause :统计 gc 的情况,(同 -gcutil )和引起 gc 的事件 -gcnew :统计 gc 时,新生代的情况 -gcnewcapacity :统计 gc 时,新生代 heap 容量 -gcold :统计 gc 时,老年区的情况 -gcoldcapacity :统计 gc 时,老年区 heap 容量 -gcpermcapacity :统计 gc 时, permanent 区 heap 容量 jstack 、 jhat  ……
(二) java 内存的管理与调配 常用的工具 序号 查看分析工具 说明 1 输出 GC 日志 参数: -XX+PrintGC  -XX:+PrintGCDetails  –XX:+PrintGCTimeStamps  -XX:+PrintGCApplicationStoppedTime  分别输出 GC 的简要信息, GC 的详细信息, GC 的时间信息及 GC 造成的应用暂停时间   -Xloggc:gc.log 输出到指定文件 gc.log  2 GC Portal 一种运行在 tomcat 上,分析 GC 日志,生成图形化报表的工具 3 JConsole 一个图形化界面,可以观察到 java 进程的 gc , class ,内存等信息。 4 JVisualVM JDK6 之后的工具,类似 JProfiler  可查看内存消耗、线程执行状态程序中 CPU ,内存的动作 5 Eclipse Memory Analyzer Eclipse 插件 , 可查看对象的内存占用状况、引用关系、分析内存泄露
(二) JVM 与线程 线程: 比进程更轻量级的调度执行单位。是 CPU 调度的最基本单位 线程创建: 1. 使用内核线程实现 2. 使用用户线程实现 3. 使用用户线程加轻量级进程混合实现 线程调度: 1. 协同式线程调度 2. 抢占式线程调度 线程状态转换: 1. 新建 2. 运行  3. 无限期等待  4. 限期等待  5. 阻塞  6. 结束
(三)线程的同步和交互 线程 在 JVM 内
(三)常见的 OOM ( 1 ) java.lang.OutOfMemoryError: Java heap space 用 new 创建对象或数组时, Java Heap 空间不足,抛出此错误。   /** *  * -Xms10M -Xmx10M -Xmn4M * */ public class HeapOOM { static class OOMObject{} public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); //int i=0; while(true) { list.add(new OOMObject()); //if(i==800000) //break; //i++; } } }
(三)常见的 OOM ( 2 ) java.lang.OutOfMemoryError: request bytes for Out of swap space JVM 内部操作(如 GC 时做 mark ) , 需要消耗堆外的内存 ,  若物理内存、 swap 区均使用完毕,那么则会出现此错误, Java 进程将会退出 。 ( 3 ) java.lang.OutOfMemoryError: PermGen space   当加载 class 时,在进行了 Full GC 后如 PermGen 空间仍然不足,则抛出此错误。
(三)常见的 OOM ( 4 )  java.lang.OutOfMemoryError: unable to create new native thread 当调用创建线程时,创建失败,会抛出此错误,如果是 JDK 内部必须创建成 功的线程,那么会造成 Java 进程退出 ;  如果是用户线程,则仅抛出 OOM; 线程太多,内存耗尽,  -Xss 调整线程所占用的栈大小 ( 5 )  java.lang.OutOfMemoryError: GC overhead limit execeeded 当通过 new 创建对象或数组时,当 Java Heap 空间不足时, GC 所使用的时间占了程序总时间的 98% ,且 Heap 剩余空间小于 2% ,则抛出此错误,以避免 Full GC 一直执行。 可通过 UseGCOverheadLimit 来决定是否开启这种策略,可通 GCTimeLimitGCHeapFreeLimit 来控制百分比。
(四)调优实例 方法区溢出实例( PermGen space ) IP : *.*.*.60 查看命令: jmap –heap  pid
(四)调优实例 方法区溢出实例( PermGen space ) IP : *.*.*.60  查看命令: jstat –gcutil  pid  1000  10
(四)调优实例 举例 一个一天 10 万 PV 的在线文档系统,硬件 4 个 CPU 、 16G 物理内存 , 操作系统 64 位 , nginx+resin 服务器  JDK1.5  -Xmx 和 -Xms 调为 12G,  使用一段时间后,经常不定期出现长时间没有响应?
(四)调优实例 举例 解决方式: 部署策略调整, nginx+5  个 resin (-Xms=-Xmx=2G 、 CMS 收集器 )
(四)调优实例 思考讨论 问题: 爱股将 ttserver 升级后,线程数瓶颈的阀值为什么会发生变化? …………………… .. 背景简介…………
(五)总结 JVM 参数调优   JVM 参数调优的目的,尽量减少 GC 所占用的时间。而现代 GC 执行的效率非常好,一般只需调整 -Xms  -Xmx  -Xmn  PermSize  MaxPermSize 并可以。
(五)总结 OOM 总结: 应用缓存或者是在一个 Collection 中保存对象,那么要确定是否有大量对象存入,大小不会无限制变大 尽可能释放无用的对象引用 一般不使用 finalize 函数 尽量避免在类的默认构造器中创建,初始大量对象 不要强制做垃圾内存的回收( System.gc() ) 尽量不要显示申请数组空间,当不得不显式申请数组空间时,尽量准确估计其合理值
Thanks!

Jvm那些事

  • 1.
    JVM 那些事 开发二部 陆松林 © All rights reserved
  • 2.
    contents (一) java 内存的管理与调配 (二) JVM 与线程 (三)常见的 OOM (四)调优实例 (五)思考总结
  • 3.
    JVM 都有哪些? 1.SunHotSpot 2.BEA Jrockit 3.IBM J9 4.apache Harmony 5.Dalvik … ..
  • 4.
    JVM 标准结构 Classloader Execution engine Runtime data area Native interface
  • 5.
  • 6.
  • 7.
    (一) java 内存的管理与调配方法区: 包括 : 类型基本信息,常量池,字段信息,方法信息,类变量,指向 ClassLoader 的引用, Class 类的引用,方法表等 java 堆: 用来存入由 new() 方式创建的对象和数组。储该对象指向方法区中类型信息的指针。 java 栈: 当 JVM 创建一个新线程时,都会产生线程计数器( PC Register )和栈。每一次方法调用都会产生栈帧,栈帧中包含局部变量区和操作数栈 。 PC 寄存器(线程计数器): 每个线程拥有自己的程序计数器,它指向下一条指令。 本地方法栈: 线程调用时 , 可接口访问运行时的数据区,直接调用本地处理器的寄存器和取任意内存。
  • 8.
  • 9.
    (一) java 内存的管理与调配内存申请 第 1 步 . 从新生代申请一块内存 第 2 步 . 新生代不够 第 3 步 . 释放不活跃的对象( minor collection ),若不够,部分活跃对象放入幸存区 第 4 步 . 旧生区空间足够时,幸存区的对象会被移到旧生区区 第 5 步 . 旧生区空间不够时, major collection 第 6 步 . 完全垃圾收集后,旧生区和幸存区无法存放对象,新生区无法创建新内存区 , 出现 OOM
  • 10.
    (一) java 内存的管理与调配内存回收 常用收集器 : 1. 引用计数收集器 2. 跟踪收集器 1. 串行 GC ( Serial Copying ) : 强引用,软引用,弱引用,虚引用。 2. 并行回收 GC(Parallel Scavenge): 多线程 3. 并行 GC(ParNew) 旧生代和持久代可用的 GC: 串行 GC(Serial MSC) : 单线程方式,耗时长 . 并行 GC(Parallel MSC) :多线程,停时间缩短。 并发 (CMS): GC 的动作和应用的动作并发,分配内存的动作和回收内存的动作同时进行。 新生可用的 GC:
  • 11.
    (一) java 内存的管理与调配常用参数说明 参数 说明 默认值 Xms/-Xmx 定义堆内存的启动值和最大值,建议在生产环境下将两值设为相同 默认是物理内存的 1/64 但小于 1G -Xmn 设置新生代的内存大小 Sun 官方推荐配置为整个堆的 3/8 -XX:PermSize/-XX:MaxPermSize 定义 Perm 段的启动值和最大值,建议在生产环境下将两值设为相同 -XX:NewRaito 设置 YOUNG 与 OLD 段的比值 1/8 -Xss 设置栈的大小 JDK5.0 以后每个线程堆栈大小为 1M ,以前每个线程堆栈大小为 256K … .
  • 12.
    (一) java 内存的管理与调配各代大小调配 新生代大小不能设置过小 新生代大小不能设置过大 避免 Survivor 区过小或过大 合理调配新生代的存活周期
  • 13.
    (一) java 内存的管理与调配常用的命令 命令 参数及说明 jmap ( linux 下特有,也是很常用的一个命令) 如: jmap -histo 2081 | jmap -histo:live 2081 -heap :打印 jvm heap 的情况 -histo :打印 jvm heap 的直方图。其输出信息包括类名,对象数量,对象占用大小。 -histo : live :同上,但是只答应存活对象的情况 -permstat :打印 permanent generation heap 情况 jstat (可以观察到 classloader , compiler , gc 相关信息) 如: jsat –gcutil 2081 1000 10 (每隔 1 秒监控一次,一共做 10 次) -gcutil :统计 gc 时, heap 情况 -class :统计 class loader 行为信息 -gc :统计 jdk gc 时 heap 信息 -compile :统计编译行为信息 -gccapacity :统计不同的 generations( 包括新生区,老年区, permanent 区)相应的 heap 容量情况 -gccause :统计 gc 的情况,(同 -gcutil )和引起 gc 的事件 -gcnew :统计 gc 时,新生代的情况 -gcnewcapacity :统计 gc 时,新生代 heap 容量 -gcold :统计 gc 时,老年区的情况 -gcoldcapacity :统计 gc 时,老年区 heap 容量 -gcpermcapacity :统计 gc 时, permanent 区 heap 容量 jstack 、 jhat ……
  • 14.
    (二) java 内存的管理与调配常用的工具 序号 查看分析工具 说明 1 输出 GC 日志 参数: -XX+PrintGC -XX:+PrintGCDetails –XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime 分别输出 GC 的简要信息, GC 的详细信息, GC 的时间信息及 GC 造成的应用暂停时间 -Xloggc:gc.log 输出到指定文件 gc.log 2 GC Portal 一种运行在 tomcat 上,分析 GC 日志,生成图形化报表的工具 3 JConsole 一个图形化界面,可以观察到 java 进程的 gc , class ,内存等信息。 4 JVisualVM JDK6 之后的工具,类似 JProfiler 可查看内存消耗、线程执行状态程序中 CPU ,内存的动作 5 Eclipse Memory Analyzer Eclipse 插件 , 可查看对象的内存占用状况、引用关系、分析内存泄露
  • 15.
    (二) JVM 与线程线程: 比进程更轻量级的调度执行单位。是 CPU 调度的最基本单位 线程创建: 1. 使用内核线程实现 2. 使用用户线程实现 3. 使用用户线程加轻量级进程混合实现 线程调度: 1. 协同式线程调度 2. 抢占式线程调度 线程状态转换: 1. 新建 2. 运行 3. 无限期等待 4. 限期等待 5. 阻塞 6. 结束
  • 16.
  • 17.
    (三)常见的 OOM (1 ) java.lang.OutOfMemoryError: Java heap space 用 new 创建对象或数组时, Java Heap 空间不足,抛出此错误。 /** * * -Xms10M -Xmx10M -Xmn4M * */ public class HeapOOM { static class OOMObject{} public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); //int i=0; while(true) { list.add(new OOMObject()); //if(i==800000) //break; //i++; } } }
  • 18.
    (三)常见的 OOM (2 ) java.lang.OutOfMemoryError: request bytes for Out of swap space JVM 内部操作(如 GC 时做 mark ) , 需要消耗堆外的内存 , 若物理内存、 swap 区均使用完毕,那么则会出现此错误, Java 进程将会退出 。 ( 3 ) java.lang.OutOfMemoryError: PermGen space 当加载 class 时,在进行了 Full GC 后如 PermGen 空间仍然不足,则抛出此错误。
  • 19.
    (三)常见的 OOM (4 ) java.lang.OutOfMemoryError: unable to create new native thread 当调用创建线程时,创建失败,会抛出此错误,如果是 JDK 内部必须创建成 功的线程,那么会造成 Java 进程退出 ; 如果是用户线程,则仅抛出 OOM; 线程太多,内存耗尽, -Xss 调整线程所占用的栈大小 ( 5 ) java.lang.OutOfMemoryError: GC overhead limit execeeded 当通过 new 创建对象或数组时,当 Java Heap 空间不足时, GC 所使用的时间占了程序总时间的 98% ,且 Heap 剩余空间小于 2% ,则抛出此错误,以避免 Full GC 一直执行。 可通过 UseGCOverheadLimit 来决定是否开启这种策略,可通 GCTimeLimitGCHeapFreeLimit 来控制百分比。
  • 20.
    (四)调优实例 方法区溢出实例( PermGenspace ) IP : *.*.*.60 查看命令: jmap –heap pid
  • 21.
    (四)调优实例 方法区溢出实例( PermGenspace ) IP : *.*.*.60 查看命令: jstat –gcutil pid 1000 10
  • 22.
    (四)调优实例 举例 一个一天10 万 PV 的在线文档系统,硬件 4 个 CPU 、 16G 物理内存 , 操作系统 64 位 , nginx+resin 服务器 JDK1.5 -Xmx 和 -Xms 调为 12G, 使用一段时间后,经常不定期出现长时间没有响应?
  • 23.
    (四)调优实例 举例 解决方式:部署策略调整, nginx+5 个 resin (-Xms=-Xmx=2G 、 CMS 收集器 )
  • 24.
    (四)调优实例 思考讨论 问题:爱股将 ttserver 升级后,线程数瓶颈的阀值为什么会发生变化? …………………… .. 背景简介…………
  • 25.
    (五)总结 JVM 参数调优 JVM 参数调优的目的,尽量减少 GC 所占用的时间。而现代 GC 执行的效率非常好,一般只需调整 -Xms -Xmx -Xmn PermSize MaxPermSize 并可以。
  • 26.
    (五)总结 OOM 总结:应用缓存或者是在一个 Collection 中保存对象,那么要确定是否有大量对象存入,大小不会无限制变大 尽可能释放无用的对象引用 一般不使用 finalize 函数 尽量避免在类的默认构造器中创建,初始大量对象 不要强制做垃圾内存的回收( System.gc() ) 尽量不要显示申请数组空间,当不得不显式申请数组空间时,尽量准确估计其合理值
  • 27.