JVM相关

2020-05-12  本文已影响0人  万福来

JVM相关

JVM - 内存结构

image.png

方法区和堆是所有线程共享的内存区域,java栈、本地方法栈和程序计数器是运行时线程私有的内存区域

Java对象分配规则

JVM - 类的加载机制

什么是类的加载

类的加载指将类的.class文件二进制数据读入到内存中,将其放在运行时的方法区内,然后在堆内存创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。Class对象封装了类在方法区内的数据结构,并提供了各种访问接口。

类的加载过程

加载 --> 验证 --> 准备 --> 解析 --> 初始化 --> 使用 --> 卸载

  1. 加载:通过类加载器加载class文件,并在堆内存创建Class对象;
  2. 验证:验证文件格式、元数据、字节码和符号引用等是否符合JAVA规范,否则报错;
  3. 准备:初始化类的静态变量和默认值;
  4. 解析:将常量池内的符号引用替换为直接引用;
  5. 初始化:将类中静态变量赋予初始值;
  6. 使用:new出对象并使用;
  7. 卸载:执行垃圾回收。

类加载器

类加载机制

类加载方式

Class.forName()和ClassLoader.loadClass()区别

JVM - Metaspace

JDK8 HotSpot JVM现在使用了本地内存来存储类元数据,被称为Metaspace,和Oracle JRockit以及IBM JVM类似。
它意味着java.lang.OutOfMemoryError:PermGen space问题会越来越少,也不再需要你去调整和监控内存空间。然而这种变化默认是可不见的,接下来我们给你展示的,是你仍然需要关注类元数据内存占用。请记住,这些新特点并不会很神奇的消除类和类加载器的内存泄露。你需要使用不同的方法和学习新的命名约定来找出问题的根源。

总结:

  1. 持久代场景
    • 这块内存区域被完全移除。
    • PermSize和MaxPermSize JVM 参数会被忽略,并且在启动的时候会给出警告信息。
  2. Metaspace 内存分配模型
    • 对于类元数据的大多数内存分配都不会发生在本地内存。
    • 被用于描述类元数据的类对象被移除。
  3. Metaspace 容量
    • 默认的,类元数据分配限制于可用的本地内存 (容量大小依赖于你用32位jvm或者64位jvm的操作系统可用虚拟内存)。
    • 新的标记已经可以使用 (MaxMetaspaceSize),它允许你限制用于类元数据的本地内存大小。如果你没有指定这个标记,Metaspace会根据运行时应用程序的需求来动态的控制大小。
  4. Metaspace 垃圾收集
    • 一旦类元数据的使用量达到了“MaxMetaspaceSize”指定的值,对于无用的类和类加载器,垃圾收集此时会触发。
    • 为了控制这种垃圾收集的频率和延迟,合适的监控和调整Metaspace非常有必要。过于频繁的Metaspace垃圾收集是类和类加载器发生内存泄露的征兆,同时也说明你的应用程序内存大小不合适,需要调整。
  5. Java 堆空间影响
    •一些杂项数据被移到了Java堆空间。这意味着当你更新到JDK8后会观察到Java堆空间的增长。
  6. Metaspace 监控
    • Metaspace 的使用可以通过HotSpot 1.8的详细的GC日志输出观察到。
    • 在基于b75上测试的时候Jstat 和 JVisualVM 还没有更新,旧的持久代空间引用依然存在。

JVM - GC算法

对象存活判断算法

GC算法

最基础的GC算法有三种:标记-清除算法、复制算法、标记压缩算法,常用的垃圾回收器一般都采用分代收集算法。

垃圾回收器

JVM - 垃圾回收器

Young GC

CMS垃圾回收器(默认75%触发)

CMS回收器在一次GC过程中会有两次STW,一次是初始标记阶段,一次是重新标记阶段

G1垃圾回收器

G1堆内存结构
默认将JVM切分为2048份固定大小区域,最小1M,最大32M,2的幂次方;区域大小通过-XX:G1HeapRegionSize参数指定
G1堆内存分配
每块区域被标记了E、S、O和H,分别映射Eden、Survivor、老年代和巨型对象区

G1提供三种垃圾回收模式 young gc 、mixed gc 和full gc

参数 含义
-XX:MaxGCPauseMillis 设置G1收集过程目标时间,默认值200ms
-XX:G1NewSizePercent 新生代最小值,默认值5%
-XX:G1MaxNewSizePercent 新生代最大值,默认值60%

JVM - 调优命令

jps

显示指定系统内所有jvm进程信息
命令格式:jps [options] [hostid]
option参数

jstat

用户监视JVM运行时状态信息的命令,可以显示出JVM的类加载、内存、垃圾收集、JIT编译等运行数据。
命令格式:jstat [option] 进程ID [interval] [count]
参数描述

S0C : survivor0区的总容量
S1C : survivor1区的总容量
S0U : survivor0区已使用的容量
S1C : survivor1区已使用的容量
EC : Eden区的总容量
EU : Eden区已使用的容量
OC : Old区的总容量
OU : Old区已使用的容量
PC 当前perm的容量 (KB)
PU perm的使用 (KB)
YGC : 新生代垃圾回收次数
YGCT : 新生代垃圾回收时间
FGC : 老年代垃圾回收次数
FGCT : 老年代垃圾回收时间
GCT : 垃圾回收总消耗时间

jhat

jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。
命令格式:jhat [dumpfile]

jstack

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
命令格式:jstack [option] 进程ID
option参数
-F : 当正常输出请求不被响应时,强制输出线程堆栈
-l : 除堆栈外,显示关于锁的附加信息
-m : 如果调用到本地方法的话,可以显示C/C++的堆栈

jinfo

jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令
命令格式:jinfo [option] [args] 进程ID
option参数
-flag : 输出指定args参数的值
-flags : 不需要args参数,输出所有JVM参数的值
-sysprops : 输出系统属性,等同于System.getProperties()

JVM - 调优工具

jconsole

Jconsole(Java Monitoring and Management Console)是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监测工具。
jconsole使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源消耗等信息。

VisualVM

VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于 Java 技术的应用程序(Java 应用程序)的详细信息。VisualVM 对 Java Development Kit (JDK) 工具所检索的 JVM 软件相关数据进行组织,并通过一种使您可以快速查看有关多个 Java 应用程序的数据的方式提供该信息。您可以查看本地应用程序以及远程主机上运行的应用程序的相关数据。此外,还可以捕获有关 JVM 软件实例的数据,并将该数据保存到本地系统,以供后期查看或与其他用户共享。

MAT

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

GChisto

GChisto是一款专业分析gc日志的工具,可以通过gc日志来分析:Minor GC、full gc的时间、频率等等,通过列表、报表、图表等不同的形式来反应gc的情况。虽然界面略显粗糙,但是功能还是不错的。

gcviewer

GCViewer也是一款分析小工具,用于可视化查看由Sun / Oracle, IBM, HP 和 BEA Java 虚拟机产生的垃圾收集器的日志,gcviewer个人感觉显示 的界面比较乱没有GChisto更专业一些。

GC Easy

这是一个web工具,在线使用非常方便.
地址: http://gceasy.io
进入官网,讲打包好的zip或者gz为后缀的压缩包上传,过一会就会拿到分析结果。

什么是压缩指针:

通常64位JVM消耗的内存会比32位的最多会多用1.5倍,这是因为对象指针在64位架构下,对象指针长度会翻倍。 对于那些将要从32位平台移植到64位的应用来说,平白无辜多了1/2的内存占用,这是开发者不愿意看到的。 幸运的是,从JDK 1.6 update14开始,64 bit JVM正式支持了 -XX:+UseCompressedOops (需要jdk1.6.0_14) 这个可以压缩指针,起到节约内存占用的新参数。

什么是OOP?

OOP = "ordinary object pointer" 普通对象指针。启用CompressOops后,会压缩的对象:

CompressedOops的原理:

32位内最多可以表示4GB,64位地址分为堆的基地址+偏移量,当堆内存<32GB时候,在压缩过程中,把偏移量/8后保存到32位地址。在解压再把32位地址放大8倍,所以启用CompressedOops的条件是堆内存要在4GB*8=32GB以内。所以压缩指针之所以能改善性能,是因为它通过对齐(Alignment),还有偏移量(Offset)将64位指针压缩成32位。换言之,性能提高是因为使用了更小更节省空间的压缩指针而不是完整长度的64位指针,CPU缓存使用率得到改善,应用程序也能执行得更快。

零基压缩优化(Zero Based Compressd Oops):

零基压缩是针对压解压动作的进一步优化。 它通过改变正常指针的随机地址分配特性,强制堆地址从零开始分配(需要OS支持),进一步提高了压解压效率。要启用零基压缩,你分配给JVM的内存大小必须控制在4G以上,32G以下。如果GC堆大小在4G以下,直接砍掉高32位,避免了编码解码过程 如果GC堆大小在4G以上32G以下,则启用UseCompressedOop 如果GC堆大小大于32G,压指失效,使用原来的64位(所以说服务器内存太大不好......)。

适用场景:

CompressedOops,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价。 不过,它的实现方式是在机器码中植入压缩与解压指令,可能会给JVM增加额外的开销。

上一篇 下一篇

猜你喜欢

热点阅读