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. Delphi xe7 FireMonkey / Mobile (Android, iOS)生成 QR Code完整实例
  2. WPF/Silverlight深度解决方案:(十六)传值实现
  3. spring security实现登录验证以及根据用户身份跳转不同页面
  4. linux awk 内置变量使用介绍
  5. 阿里云k8s私有仓库registry操作管理
  6. Educoder 机器学习之随机森林算法 第3关:手写数字识别
  7. java txt中统计一个字母出现的次数并储存,统计txt文件中每个字符出现的次数,并根据次数从高到低排序...
  8. FPGA实现“打字机”(VGA UART)
  9. java虚拟机相关知识点(全网最全)
  10. spring security 实现微信登录
  11. 除了汽车,自动驾驶还将颠覆这33个行业……
  12. mybatisplus执行自定义sql时,多出一个limit
  13. 初学者如何3步入门选吉他,附上新手购琴常见问题和吉他品牌推荐
  14. 软件测试面试刁难人?花重金购买的资料1套全给你解决
  15. 多个视频如何合 并,怎么合成视频
  16. 医疗IT系统在手术部供配电间的应用
  17. NET:Error Creating Control -Object Reference Not Set To An Instance Of Object
  18. [Android]iTextG与SpongyCastle踩坑经历
  19. 2021年全球与中国生物芯片行业市场规模及发展前景分析
  20. 2017年大计基算法题 题解

热门文章

  1. html超链接点不了_HTML、CSS、JS都有哪些区别?不看必悔
  2. python架构图教程_Python 五分钟绘制漂亮的系统架构图
  3. Oracle10g数据库的完全卸载:
  4. 变量的初始化与使用C语言程序注释,c - (为什么)使用未初始化的变量未定义行为?...
  5. leetcode235. 二叉搜索树的最近公共祖先
  6. Redis集群添加节点
  7. 关于关闭SELinux的方法
  8. 剑指offer_02
  9. Redis Mac下安装与使用
  10. 研发团队平稳度过“从小到大”并非易事