1 此文目的

本文不准备从盘古开天地开始讲述JVM的种种,相关的文章网上太多了,大多也无非转来转去,连图都差不多。笔者只整理个提纲挈领的学习路线指南,并对自己学习过程中遇到的坑和容易混淆和忽视的地方作个总结。

2 JVM内存模型

2.1 内存模型

内存区域划分有多个维度,相同区域在不同维度的名称并不一样。如下图所示

可以看到,survivor区被划分为了survivor0和survivor1两个区域,但是在讲MinorGC的原理时,我们又会说survvior to和survivor from两个区域。事实上,survivor0和survivor1是物理维度的划分,而survivor to和survivor from是逻辑维度的划分,在MinorGC的过程中,survivor0和survivor1交替担当to区和from区。 来仔细解释一下MinorGC的过程: 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。大致如下图所示:

2.2 方法区和永久代

这两个概念,很多时候都被当做是同一个概念。实际上,“方法区”是java虚拟机规范中对存放类信息,字段,方法,常量,静态变量,接口和常量池的内存区域的定义,而“永久代”则是HotSpot VM在1.8版本以前对于方法区的具体实现。由于java虚拟机规范并没有对方法区的具体实现作限制,所以HotSpot VM和JRocket VM对于方法区的实现都是不一样的,JRocket中就没有永久代的概念。而在1.8及1.8以后的版本中,HotSpot VM用"元空间"--metaspace来代替永久代,实现方法区。 这个变化带来的就是VM参数的变化,所有的PermGen都被替换成了MetaSpace。并且metaSpace不再使用堆内存,而是使用系统内存。但是该发生的OOM一样会发生。原因也基本都是加载到内存中的 class 数量太多或者体积太大。

3.GC

3.1 GC算法

GC算法和GC收集器也是两个维度的概念。 GC算法包括清除算法(也叫标记清除算法),复制算法,标记-整理算法。 不同垃圾收集器针对不同的内存区域,采用不同的GC算法。 具体介绍,网上相关资料很多,可以参考这篇文章:blog.csdn.net/xiaoping091…

3.2 垃圾收集器

垃圾收集器经历了从串行收集器到并行收集器,再到并发收集器的进化过程。这三者的区别如下图所示

串行和并行的区别比较容易理解,而CMS垃圾收集器的原理要注意的是,虽然它是并发收集器,但它的GC线程并不是完完全全地与应用的进程并发进行,它只是通过用两次短暂停来代替并行GC的一次长暂停,以期达到减少应用线程暂停的目的,详见CMS垃圾回收机制

不同版本默认使用的垃圾收集器以及支持开发者定制的垃圾收集器都是不一样的 jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) jdk1.9 默认垃圾收集器G1 与此同时,通过设置JVM参数也可以自己选择垃圾收集器。如要开启G1垃圾回收器,可以用-XX:+UseG1GC,支持G1垃圾回收器的JDK最低版本为JDK 7u4。在用户自己选择垃圾收集器的时候,要注意JDK版本的问题。 笔者用表格的形式列出了新生代和老年代的GC收集器的常见搭配方案:

3.3 Full GC触发条件

频繁FullGC导致的stop the world的现象,会大大影响系统的稳定性。尽管一代又一代的垃圾收集器的优化,使得stop the world的时间越来越短,但是在大型应用中,还是避之不及。 出发FullGC的情况有以下几种:

  1. System.gc()方法的调用
  2. 老年代不足
  3. 方法区不足
  4. concurrent mode failure concurrent mode failure是在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的(有时候“空间 不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC)。
  5. promotion failed minor gc时年轻代的存活区空间不足而晋升老年代,老年代又空间不足而触发full gc
  6. 统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间 当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC(因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先触发一次单独的young GC)。

3.3.1 OOM的类型

通常情况下,JVM的GC机制能保证应用的正常运行,导致系统频繁FullGC的原因百分之九十都是内存溢出(OOM)。OOM分为以下几类:

  1. Java.lang.OutOfMemeoryError:Java heap space 堆空间的内存溢出,可能的原因是某个可达性分析认为不能被回收的对象随着时间推移变得越来越大,例如某个static类型的map对象,被不停地塞入键值对,也可能是大循环或者死循环不断创建对象,而对象分配内存的速度超过了GC清理内存的速度。
  2. Java.lang.OutOfMemeoryError:GC overhead limit exceeded 这种OOM异常是Hotspot VM 1.6定义的一个策略,通过统计GC时间来预测是否要OOM了,提前抛出异常,防止OOM发生。Sun 官方对此的定义是:“并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作。” 那么为什么会出现这种GC效率低下的现象呢?通常是因为老年代内存占有过多导致的频繁GC,这种情况下,可以增加-verbose:gc -XX:+PrintGCDetails来分析具体原因,也可以加-XX:+HeapDumpOnOutOfMemoryError,这样OOM时会自动做Heap Dump,第二种方法适用于所有OOM异常的排查。
  3. Java.lang.OutOfMemoryError: PermGen space(JAVA8引入了Metaspace区域)方法区内存溢出,通常是因为加载的类过多,可以先排除程序问题导致的重复类加载,或者加大方法区内存的分配
  4. Java.lang.OutOfMemoryError: unable to create new native thread 产生这种异常的原因是由于系统在不停地创建大量的线程,且不进行释放。

4. JVM调优

4.1 调优参数

正确设置JVM参数,可以尽可能多地避免系统资源浪费,尽可能详细地掌握系统运行情况,并且对可能出现的问题防患于未然。

Xms:堆初始空间

Xmx:堆最大空间

Xmn:年轻代大小

XX:MaxNewSize 新生代最大空间 建议设置为整个堆的1/3到1/4

XX:NewSize

XX:MaxTenuringThreshold survivor中到老年代中的年龄阈值

Xss:每个线程的栈大小

java -XX:+PrintCommandLineFlags -version 得到JDK建议的内存分配大小

tomcat设置catalina.sh:

export JAVA_OPTS="-server –Xms1024m -Xmx1024m -XX:+UseParallelOldGC -verbose:gc -Xloggc:../logs/gc.log

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps"

-XX:+PrintCommandLineFlagsjvm参数可查看默认设置收集器类型

-XX:+PrintGCDetails亦可通过打印的GC日志的新生代、老年代名称判断

4.2 JVM监控

  • 1.本机环境下,推荐一款idea上的插件VisualVM Launcher,实际就是联动了JDK开发包中自带的jvisualvm.exe监控软件。也可以设置远程监控。具体使用方法,可以参考这篇文章https://blog.csdn.net/wngpenghao/article/details/82884874IDEA Java性能分析插件VisualVM Launcher 配置(JAVA VisualVM 与Jconsole配置相同)

  • 2.Linux的相关命令: jstat命令可以对jvm从各维度进行统计,详细使用参考jstat命令查看jvm的GC情况

  • 3.VM参数设置时,指定打印出gc日志 -Xloggc:../logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 详细的参数设置以及gc日志该如何阅读,可以参考java之GC日志该怎么看

4.3 JVM异常排查

  1. 保存dump 当使用监控软件或者命令查看发现JVM异常时,应第一时间保存下dump现场。 命令是jmap -dump:format=b,file=文件名[pid] pid是服务进程

如果是使用jvisualvm就更方便了,直接点击如图所示的按钮即可:

2. 分析dump eclipse有一款插件叫做Memory Analyzer(MAT),但是目前idea并没有这款插件 此外,jhat是sun 1.6及以上版本中自带的一个用于分析JVM 堆DUMP 文件的工具,基于此工具可分析JVM HEAP 中对象的内存占用情况 jhat -J-Xmx1024M [file] 执行后等待console 中输入start HTTP server on port 7000 即可使用浏览器访问 IP:7000 可以特别关心下图标出的这个选项 这对于排查堆内存溢出非常有效

4.4 实战例子

由于实际工作中,能接触到JVM机会的机会并不多,所以笔者整理了一些经典实例

Metaspace溢出排查过程

分享一次 Java 内存泄漏的排查

一次生产的 JVM 优化案例

JVM成长之路,记录一次内存溢出导致频繁FGC的问题排查及解决

非常详细的jvm调优实例,性能瓶颈定位

转载于:https://juejin.im/post/5cf500c7f265da1b855c40c3

JVM原理探究及调优方法论相关推荐

  1. 【Elasticsearch】Elasticsearch高级调优方法论之——根治慢查询!

    1.概述 转载:Elasticsearch高级调优方法论之--根治慢查询! 1.引言 Elasticsearch是非常灵活且功能丰富的搜索引擎,它提供了许多不同查询数据的方法.在实战业务场景中,经常会 ...

  2. jvm原理及性能调优系列(jvm调优)

    jvm原理及性能调优系列(jvm调优) JVM设置: 1.设置合适的最大堆内存(新生代和老生代的最大和值)和最小堆内存(jvm启动时占用的操作系统内存大小),及设置好堆的比例分配. 2.设置合适的新生 ...

  3. JVM原理讲解和调优

    一.什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现 ...

  4. JVM原理及性能调优

    注明:转载文章.好记性不如烂笔头呀~ 一.什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际 ...

  5. JVM原理和性能调优

    JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂界 ...

  6. Jvm原理剖析与调优之内存结构

    一些不得不说的概念 JVM JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个 ...

  7. unix查询内存使用率_Elasticsearch高级调优方法论之——根治慢查询

    一.描述 Elasticsearch是非常灵活且功能丰富的搜索引擎,它提供了许多不同查询数据的方法.在实战业务场景中,经常会出现远远低于预期查询速度的慢查询.作为分布式系统的Elasticsearch ...

  8. jvm原理与性能调优

    文章目录 一.JVM内存结构 1.运行时数据区 2.直接内存 二.JVM中的对象 1.对象的创建 2.对象的内存布局 3.对象的访问定位 三.垃圾回收算法和垃圾回收器 1. 如何判断对象是已死 2.分 ...

  9. 【JVM调优】JVM原理与性能调优

    一.参考资料 今日头条https://www.toutiao.com/i7007696978586976805

最新文章

  1. C#多线程学习5——多线程的自动管理(定时器)
  2. linux 源码安装详解
  3. java oracle 乐观锁,oracle为什么默认乐观锁
  4. Linux上用Jenkins执行shell
  5. pandas DataFrame数据筛选和切片
  6. (转)JSP详细教程
  7. IDEA跟金山词霸的小bug
  8. xp 架设网站服务器,WinXP如何设置iis服务器?WinXP iis服务器设置教程
  9. MOS管当开关控制时,为什么一般用PMOS做上管NMOS做下管?
  10. 华为手机怎样无线与电脑连接服务器,华为手机如何与电脑远程连接服务器
  11. 14 Python进行数据乱码处理
  12. MySQL 8.0 初学与基础项目实践
  13. 【macOS 个性化技巧】如何更换 macOS Mojave 登陆界面背景图?
  14. 原型图到成品:步步深入 CSS 布局
  15. 云原生与微服务架构基础:01 | 为什么说云原生重构了互联网产品开发模式
  16. Castor xsd生成java,Can Castor handle class generation from multiple XSDs importing from a base XSD?...
  17. android免root运行adb高级权限命令,例如修改手机设置等(转)
  18. 类似于PMO,有关CAll center的职位
  19. Unity---图形学基础
  20. BPDC1000-DE回馈式电子负载常见问答

热门文章

  1. java11创建项目_2019-04-11 使用IDEA创建SpringBoot项目
  2. * 构建一个list集合存储5个学生对象, 过滤年龄小于18的,存入一个新的map集合
  3. Maven学习记录之maven基本操作命令,maven本地工厂的创建,maven骨架的生成,以及在eclipse中创建maven工程:...
  4. 尼尔机器人技能快捷键_《尼尔机械纪元》连招操作技巧
  5. 《兔兔公司的历史》那些年,百度的荣耀和沉沦
  6. 算法(27)-最大系列
  7. 算法(5)-leetcode-explore-learn-数据结构-字符串
  8. Android studio安装过程中入的坑的记录与记录
  9. 《深入理解JVM.2nd》笔记(五):调优案例分析与实战
  10. 《Python Cookbook 3rd》笔记(5.7):读写压缩文件