深入了解Java虚拟机:JVM调优

初探

  • Java代码运行,Java源文件(.java)-- > javac编译 -->字节码文件(.class)–> JVM --> 计算机底层指令和硬件
  • jvm,Java虚拟机,不同的操作系统有不同版本的JVM虚拟机。JVM从软件层面,隐藏了底层技术实现的复杂性,屏蔽了不同机器不同操作系统的底层细节,保证Java代码,编写一次,到处执行。Write Once,Run Anywhere.

JDK、JRE与JVM

  • Java,是一种面向对象的编程语言,属于高级语言。1995年,Java之父,詹姆斯·高斯林(高司令),Oak --> Java。
  • JDK Java开发工具包,包含JRE。
  • JRE Java运行时环境,包含JVM。
  • JVM Java虚拟机,包含运行时数据区、执行引擎、本地库接口等。

内存结构

  • 堆和方法区,是线程共享区
  • 栈,本地方法栈、程序计数器,是线程独占区

  • new的对象都放在堆里
年轻代(占堆内存的1/3)
  • Eden,占年轻代的80%,程序数据先往Eden里写入,当Eden内存用完时,触发Minor GC,垃圾对象被清理,非垃圾对象被复制到Survivor区域,这些对象的分代年龄加1。
  • Survivor,占年轻代的20%,分为2部分,各占10%;当触发Minor GC 时,Survivor区域里的垃圾对象也会被清理,非垃圾对象在Survivor的两个分区相互移动,这些对象的分代年龄继续加1。
  • S0 1/3 * 10%
  • S1 1/3 * 10%
老年代(占堆内存的2/3)
  • 大对象直接进入老年代,可以通过 -XX:PretenureSizeThreshold 设置,超过这个值,对象直接在Old区分配内存。这个参数只在Serial和ParNew2个收集器下有效。
  • 长期存活的对象进入老年代。当Survivor区里的非垃圾对象分代年龄达到阈值(默认15,可通过 -XX:MaxTenuringThreshold 来设置),就会将这些对象移入老年代。
  • 对象动态年龄判断。Minor GC时,当一批对象的总大小大于Survivor区内存大小的50%(可通过 -XX:TargetSurvivorRatio指定),会被放入Old区分配内存。
  • Minor GC后,Survivor区放不下,多余的部分也会放入到Old区域。
  • 当老年代内存被占用完,就会触发full GC。
  • 当full GC 后,old区域空间还是不够分配,程序抛出异常OutOfMemoryError: Java heap space。

  • 栈,遵循后进先出原则
  • 一个方法,分配一个栈帧
  • 局部变量表,存储方法的局部变量,存储变量的指针
  • 操作数栈,存储变量的值
  • 动态链接
  • 方法出口,方法运行结束,返回主线程

程序计数器

  • 当前线程正在运行的代码的位置(行号)

方法区

  • 存储类信息,类的常量和静态变量

本地方法栈

  • native 本地方法,调用本地系统的接口或者本地外部c程序的接口

垃圾回收机制

  • GC Roots根节点:线程栈的本地变量,静态变量、本地方法栈的变量等等
  • 讲“GC Roots”对象作为起点,从这些节点开始搜索引用的对象,找到的对象标记为非垃圾对象,其余未标记的对象都是垃圾对象。
  • Eden满时触发Minor GC
  • Old满时触发full GC

性能监控工具

jconsole

  • java启动命令添加jvm参数 -XX:+HeapDumpOnOutOfMemoryError 可以输出内存溢出时的堆栈

jmap

  • 查看堆内存使用情况
  • 命令 : jmap -heap

jvisualvm

  • java 6,7,8自带的jvm调优工具
  • java9及以后,就停止使用java VisualVM了,改用Graal VisualVM了
  • 查看heap堆得Eden、Old,需要安装一个Visual GC插件。安装方法很简单,找到jdk目录的bin下面,打开jvisualvm.exe,然后 工具-》插件,选择Visual GC即可,在线安装如果不行,可以先下载再安装。
    jvisualVM界面

性能调优案例

概念

  • STW : Java中Stop-The-World机制.
  • 执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起,只执行垃圾收集。这是Java中一种全局暂停现象,会给程序使用者带来非常不好的体验(卡顿)。
  • jvm调优,就是为了减少full GC 的次数,减少STW时间,发挥出机器原本就有的性能,使程序更加稳定高效的运行,最终给程序使用者带来更好的使用体验。

部分JVM参数

  • Xms 设置java程序启动时堆内存128M(默认为物理内存1/64,且小于1G)
  • Xmx 设置最大堆内存256M,超出后会出现 OutOfMemoryError(默认为物理内存1/64,且小于1G)
  • Xmn 堆内存,新生代区域大小
  • Xss 设置线程栈的大小 1M(默认1M)
  • XX:MinHeapFreeRatio=40:设置堆空间最小空闲比例(默认40)(当-Xmx与-Xms相等时,该配置无效)
  • XX:MaxHeapFreeRatio=70:设置堆空间最大空闲比例(默认70)(当-Xmx与-Xms相等时,该配置无效)
  • XX:NewRatio=2:设置年轻代与年老代的比例为2:1
  • XX:SurvivorRatio=8:设置年轻代中eden区与survivor区的比例为8:1:1
  • XX:MetaspaceSize=64M:设置元数据空间初始大小(取代-XX:PermSize)
  • XX:MaxMetaspaceSize=128M:设置元数据空间最大值(取代之前-XX:MaxPermSize)
  • XX:TargetSurvivorRatio=50:设置survivor区使用率。当survivor区达到50%时,将对象送入老年代

调优

  • 可以根据实际需要分配堆内存大小,运行程序,触发full GC,再根据实际情况调优
  • 如果条件足够,可以多分配点内存。可以适当调大Eden,让更少的临时对象进入Old区。
  • 可以考虑使用G1
  • 可以参考这篇博客,讲的比较详细。
发布了83 篇原创文章 · 获赞 75 · 访问量 31万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章