JVM结构化体系

目录

目录

1.JVM 简介

1.1. 如何理解 JVM 呢?

 1.2. 市场主流 JVM 分析?

1.3. 为什么要学习 JVM?

1.4. 字节码底层是如何执行呢?

如何理解 JIT 呢?

为什么 JVM 中解释执行与编译执行的并存(混合模式)?

1.5. 如何理解 JVM 的运行模式?

如何查看现在 JVM 的工作模式?

2.JVM 体系结构

2.1. JVM 的产品架构是怎样的?

2.2. JVM 运行时内存结构是怎样的?

2.2.1. JVM 线程共享区应用分析

1. Heap (堆内存):

2. Method Area (方法区)

2.2.2. JVM 线程私有区应用分析

1. Program Counter Register (程序计数器)

2. Stack Area (虚拟机栈区)

3. Native Stack Area (本地方法栈)

 3.JVM 应用参数分析

3.1. 如何理解 JVM 中的内存溢出?

代码演示:堆内存溢出

代码演示:元数据内存溢出(JDK8)

3.2. JVM 工具应用分析篇

3.2.1. 命令行工具篇

1. Jps [options] [hostid] (hostid 为 ip 或域名地址)

2. jmap -heap pid

3. jstack 用于生成 java 虚拟机当前时刻的线程快照


1.JVM 简介

1.1. 如何理解 JVM 呢?

JVM(Java Virtual Machine) 是:
1) JAVA 平台的一部分,是一种能够运行 Java bytecode 的虚拟机。
2) 是硬件计算机的抽象 ( 虚构 ) 实现,可以解释执行 JAVA 字节码。
3) 是实现 JAVA 跨平台运行的基石。

 1.2. 市场主流 JVM 分析?

JVM 是一种规范基于这种规范,不同公司就对此规范做了具体实现,例如市场上
的一些主流 JVM 如下:
1. JRockit VM (BEA 公司研发,后在 2008 年由 Oracle 公司收购 )
2. HotSpot VM (Sun 公司研发,后在 2010 年由 Oracle 公司收购 )
3. J9 VM IBM 内部使用)
说明: HotSpot 目前是应用最官方,最主要的一款 JVM 虚拟机

1.3. 为什么要学习 JVM

深入理解 JVM 可以帮助我们从平台角度提高解决问题的能力,例如:
1) 有效防止内存泄漏( Memory leak
2) 优化线程锁的使用 (Thread Lock) 齐雷 qilei@tedu.cn
1-4
3) 科学进行垃圾回收 (Garbage collection)
4) 提高系统吞吐量 (throughput)
5) 降低延迟 (Delay) ,提高其性能 (performance)

1.4. 字节码底层是如何执行呢?

JAVA 源程序编译,执行过程如下图所示:

在主流的 JVM (例如 HotSpot )中 , class 文件翻译成机器码执行时提供了两
种方式,如下图所示:1
其中:
1) 热点代码: 一般泛指循环或高频使用的方法。
2) 解释执行器:负责逐条将字节码翻译成机器码并执行。
3) 编译执行器:负责即时编译 (Just-In-Time compilation,JIT) 执行。

如何理解 JIT 呢?

JIT( 即时编译 ) 是用来提高 java 程序运行效率的一种技术 , 基于这种技术,可以
字节码编译成平台相关的原生机器码,并进行各个层次的优化,这些机器码会被
缓存起来,以备下次使用。

为什么 JVM 中解释执行与编译执行的并存(混合模式)?

解释器与编译器两者各有优势,当程序需要迅速启动和执行的时候,解释器可以
首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,
即时编译( JIT )逐渐发挥作用,它可以对反复执行的热点代码以方法为单位进
行即时编译,可以获取更高的执行效率。但是如果 JIT 对每条字节码都进行编
译,缓存 ( 缓存的指令是有限的 ) ,会增加开销。所以当程序运行环境中内存资源
限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使
用编译执行来提升效率。

1.5. 如何理解 JVM 的运行模式?

JVM 有两种运行模式 Server Client。两种模式的区别在于,Client

式启动速度较快, Server 模式启动较慢;但是启动进入稳定期之后 Server
式的程序运行速度比 Client 要快很多。这是因为 Server 模式启动的 JVM 采用
的是重量级的虚拟机,对程序采用了更多的优化;而 Client 模式启动的 JVM
用的是轻量级的虚拟机。所以 Server 启动慢,但稳定后速度比 Client 远远要
快。

如何查看现在 JVM 的工作模式?

说明:现在 64 位的 jdk 中默认都是 server 模式。当虚拟机运行在 -client
模式的时候 , 使用的是一个代号为 C1 的轻量级编译器 , -server 模式启动的
虚拟机采用相对重量级 , 代号为 C2 的编译器 .c1 c2 都是 JIT 编译器, C2 C1 编译器编译的相对彻底 , 服务起来之后 , 性能更高 .

2.JVM 体系结构

2.1. JVM 的产品架构是怎样的?

JVM Java Hotspot Architecture :主要分为三大部分:
1) 类加载系统 ( ClassLoader System ) :负责加载类到内存
2) 运行时数据区 ( Runtime Data Area ):负责存储数据信息
3) 执行引擎 ( Execution Engine : 负责调用对象执行业务

2.2. JVM 运行时内存结构是怎样的?

JVM 启动运行 Class 文件时会对 JVM 内存进行切分,我们可以将其分为线程共

享区和线程独享区。如下图所示 .
其运行时内存详细架构如下:
备注:
 
1.在 JDK8 中持久代 (Permanent Generation)部分数据移到了元数据 ( Metaspace ),在 JDK8 中已经没有持久代。元空间的本质和永久代类似,都   JVM 规范中方法区的实现,不过元空间与永久代之间最大的区别在于:元空 间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受 本地内存限制,但可以通过以下参数来指定元空间的大小。

2.永久代

在自定义类加载器还不是很常见的时候,类大多是static的,很少被卸载或收集,因此被成为“永久的(Permanent)”。

同时,由于类class是JVM实现的一部分,并不是由应用创建的,所以又被认为是“非堆(Non-Heap)”内存。

在JDK8之前的HotSpot JVM,存放这些“永久的”区域叫做“永久代(permanent generation)”。

2.2.1. JVM 线程共享区应用分析

1. Heap (堆内存)
堆内存概要:
1) 虚拟机启动时创建,被所有线程共享 , 用于存放所有 java 对象实例 .
2) 可分为年轻代 (eden) 和老年代 (Tenured)
3) 是垃圾收集器(GC)管理的主要区域
4) 堆中没有内存分配时将会抛出 OutOfMemoryError
对象在内存中分配说明:
新创建的对象一般都分配在年轻代,当对象比较大时年轻代没有足够空间存储时还可直接分配到老年代。 有时候系统为了减少 GC 开销对于小对象且没有逃逸的对象还 可以直接在栈上分配。
堆内存大小配置参数说明:
1) -Xms 设置堆的最小空间大小。
2) -Xmx 设置堆的最大空间大小。
3) -XX:NewSize 设置新生代最小空间大小。
4) -XX:MaxNewSize 设置新生代最大空间大小。
5) -XX:NewRatio 新生代和老年代的比值,值为 4 则表示新生代 : 比老年代 1: 4
6) -XX:SurivorRatio 表示 Survivor eden 的比值,值为 8 表示两个 survivor:eden=2:8.
8) -Xss: 设置每个线程的堆栈大小。
2. Method Area (方法区)
方法区(1.8以后叫元数据区)概要:
1) 非堆内存,逻辑上的定义, 用于存储类的数据结构信息,常量、静态变量以及即时编译器编译后的代码等数据。
由于元 数据存储的信息不容易变动,因此它被安置在一块堆外内存。
2) 不同 jdk ,方法区的实现不同, JDK8(hotspot)  中的方法区对应的是 Metaspace,是一 块本地内存。
方法存储说明: 
方法区内存配置参数说明:
1) -XX:PermSize 设置永久代最小空间大小( JDK8 已弃用)。
2) -XX:MaxPermSize 设置永久代最大空间大小( JDK8 已弃用)。
3) -XX:MetaspaceSize 设置元数据区最小空间。 (JDK8)
4) -XX:MaxMetaspaceSize 设置元数据区最大空间。 (JDK8)

2.2.2. JVM 线程私有区应用分析

1. Program Counter Register (程序计数器)
1) 线程启动时创建,线程私有。
2) 用于记录当前正在执行的程序的字节码指令地址
3) Java 虚拟机规范唯一一个没有内存溢出的区域。
2. Stack Area (虚拟机栈区)
虚拟机栈概要:
1) 用于存储栈帧 (Stack Frame) 对象,保存方法的局部变量表操作数
行运行时常量池的引用一些额外的附加信息
2)每  一次方法调用都会创建一个新的栈帧,并压栈。当方法执行完毕之后,便会
将栈帧出栈。
3) 栈上对象的分配:对于小对象(一般几十个 bytes ),在没有逃逸的情况下,可以
直接分配在栈上(直接分配在栈上,可以自动回收,减轻 GC 压力);大对象
或者逃逸对象无法栈上分配
说明:方法在进行递归调用时容易出现栈内存溢出。
虚拟机栈参数说明:
1 -Xss128k : 设置每个线程的栈大小
JDK5.0 以后每个线程堆栈大小为 1M ,以前每个线程堆栈大小为 256K。可根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多 的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经 验值在 3000~5000 左右。
3. Native Stack Area (本地方法栈)

1) 为虚拟机使用到的 Native 方法提供服务。
2) 用于存储本地方法执行时的一些变量信息,操作数信息,结果信息。

 3.JVM 应用参数分析

3.1. 如何理解 JVM 中的内存溢出?

每个 Java 程序运行时都会使用一定量的内存 , JVM 内存不足以满足当前 java 程序运行时所需要的内存资源时就会出现内存溢出的现象。

代码演示:堆内存溢出

在如下代码中假如不断的向 list 集合中存储新的对象,list 集合底层也会不 断的进行扩容,当内存中没有足够连续内存 空间存储这些对象时就会出现堆内 存溢出。
public class TestOOM01 {public static void main(String[] args) {long t1=System.currentTimeMillis();try {List<byte[]> list=new ArrayList<>();for(int i=0;i<100;i++) {list.add(new byte[1024*1024]);}}finally {long t2=System.currentTimeMillis();
//System.out.println("oom:"+(t2-t1));}}
}
运行时可设置虚拟机参数
-Xmx50m -Xms10m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=e:/a.dump
说明:如上代码在运行了一段时间以后会出现堆内存溢出 (OutOfMemoryError), 可通过调整堆内存大小,延迟内存溢出的时间。

代码演示:元数据内存溢出(JDK8)

当在有限的元数据内存区不断的加载新的类时会导致元数据区空间不足从而出 现内存溢出现象,例如:
public class MetaSpaceTest {public static void main(String[] args) {int i = 0;try {for (i = 0; i < 100000; i++) {new CglibBean(new HashMap<>());}} catch (Exception e) {System.out.println(e.getMessage());} finally {System.out.println("total create count:" + i);}}public static class CglibBean {public CglibBean(Object object) {Enhancer enhancer = new Enhancer();enhancer.setUseCache(false);enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> obj);enhancer.setSuperclass(object.getClass());enhancer.create();}}
}

上述代码通过Cglib生成大量的HashMap代理,下面我们在运行这段代码的时候指定下列参数

-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails

当我们程序循环至3660次,也就是说我们大约在生成了约3660个代理类以后元数据区发生了内存溢出,下面将MaxMetaspaceSize改为50M执行,

 从上图可以看出当我们生成了1710个代理类以后元数据区发生了内存溢出,可见一个元数据区的大小决定了Java虚拟机可以装载的类的多少。

3.2. JVM 工具应用分析篇

3.2.1. 命令行工具篇

1. Jps [options] [hostid] (hostid ip 或域名地址)
jps 是用于查看有权访问的 hotspot 虚拟机的进程,当未指定 hostid 时,默认 查看本机 jvm 进程。
-q 不输出类名、 Jar 包 名和传入 main 方法的参数
-m 输出传入 main 方法的参数
-l 输出 main 类或 Jar 的全限名
-v 输出传入 JVM 的参数
 例如:
代码示例
执行:
1)Jps -l 输出应用程序主类完整 package 名称或 jar 完整名称 .
2)Jps -v 列出 jvm 参数
2. jmap -heap pid
用于打印指定 Java 进程的对象内存映射或堆内的细节。
代码案例
public class TestCommand {public static void main(String[] args) {while (true) {byte[] b1 = new byte[1024 * 1024];}}}
例如获取如上代码的进程 id 之后,执行 jmap -heap 24966 可检测堆的配置 信息,还可以借助 jmap -histo:live 1963 快速定位内存泄露。
其中:
1) MaxHeapFreeRatio:  最大空闲堆内存比例
最大空闲对内存比例, GC 后如果发现空闲堆内存大于整个预估堆内存的 N%(百分比 ) JVM 则会收缩堆内存,但不能小于 -Xms 指定的最小堆的限制。
2) MinHeapFreeRatio: 最小空闲堆内存比例
GC 后如果发现空闲堆内存小于整个预估堆内存的 N%( 百分比 ), JVM 会增大 堆内存,但不能超过 -Xmx 指定的最大堆内存限制。
3) MaxHeapSize: -Xmx, 堆内存大小的上限
4) InitialHeapSize: -Xms, 堆内存大小的初始值
5) NewSize: 新生代预估堆内存占用的默认值
6) MaxNewSize: 新生代占整个堆内存的最大值
7) OldSize: 老年代的默认大小 ,
8) NewRatio:
老年代对比新生代的空间大小 , 比如 2 代表老年代空间是新生代的两倍大小 .
9) SurvivorRatio:
Eden/Survivor 的值 . 例如 8 表示 Survivor:Eden=1:8, 因为 survivor   2 , 所以 新生代  Eden 的占比为 8/10 
10)MetaspaceSize:
分配给类元数据空间的初始大小 (Oracle 逻辑存储上的初始高水位,the initial high-water-mark ). 此值为估计值 . MetaspaceSize 设置得过大,会延长垃圾回收时间 . 垃圾回收过后 , 引起下一次垃圾回收的类元数据空间的 大小可能会变大
11)MaxMetaspaceSize:
是分配给类元数据空间的最大值 , 超过此值就会触发 Full GC. 此值仅受限系统内存的大小 , JVM 会动态地改变此值
12)CompressedClassSpaceSize:
类指针压缩空间大小 , 默认为 1G.
13)G1HeapRegionSize:
G1 区块的大小 , 取值为 1M 32M. 其取值是要根据最小 Heap 大小划分出 2048 个区块 .
说明 :jmap 在系统调优时通常会结合 jhat 来分析 jmap 生成的 dump 文件。
3. jstack 用于生成 java 虚拟机当前时刻的线程快照
生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
示例代码:
class SyncTask02 implements Runnable {private List<Integer> from;private List<Integer> to;private Integer target;public SyncTask02(List<Integer> from, List<Integer> to, Integer target) {this.from = from;this.to = to;this.target = target;}@Overridepublic void run() {moveListItem(from, to, target);}private static void moveListItem(List<Integer> from,List<Integer> to, Integer item) {log("attempting lock for list", from);synchronized (from) {log("lock acquired for list", from);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log("attempting lock for list ", to);synchronized (to) {log("lock acquired for list", to);if (from.remove(item)) {to.add(item);}log("moved item to list ", to);}}}private static void log(String msg, Object target) {System.out.println(Thread.currentThread().getName() +": " + msg + " " +System.identityHashCode(target));}
}public class TestDeadLock02 {public static void main(String[] args) {List<Integer> list1 = new ArrayList<>(Arrays.asList(2, 4, 6, 8,10));List<Integer> list2 = new ArrayList<>(Arrays.asList(1, 3, 7, 9,11));Thread thread1 = new Thread(new SyncTask02(list1, list2, 2));Thread thread2 = new Thread(new SyncTask02(list2, list1, 9));thread1.start();thread2.start();}
} 
例如 执行 jstack -l 22345 ,检测到死锁问题:
3.2.2. GUI 工具篇:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/821006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】类和对象③(类的默认成员函数:拷贝构造函数 | 赋值运算符重载)

&#x1f525;个人主页&#xff1a;Forcible Bug Maker &#x1f525;专栏&#xff1a;C 目录 前言 拷贝构造函数 概念 拷贝构造函数的特性及用法 赋值运算符重载 运算符重载 赋值运算符重载 结语 前言 本篇主要内容&#xff1a;类的6个默认成员函数中的拷贝构造函数…

el-drawer二次封装进行可拖拽

1.想要的效果 鼠标放到上面出现箭头显示可拖拽得图标 2.代码实现 2.1封装成自定义指令 // drawerDragDirective.js // 定义指令 const drawerDragDirective {// 指令绑定时的处理函数bind(el, ) {const minWidth 300;const dragDom el.querySelector(.el-drawer);// 创…

掀起区块链开发狂潮!Scaffold-eth带你一键打造震撼DApp

文章目录 前言一、Scaffold-eth是什么&#xff1f;二、安装和配置1.准备工作2.安装3.配置开发环境 三、进阶使用1.放入自己的合约2.部署运行 总结 前言 前面的文章传送&#x1f6aa;&#xff1a;hardhat入门 与 hardhat进阶 在之前的文章中&#xff0c;我们已经探讨了使用Har…

设计模式系列:简单工厂模式

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS二次开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 定义 特点 使用场景 优缺点 (1) 优点…

故障转移-redis

4.4.故障转移 集群初识状态是这样的&#xff1a; 其中7001、7002、7003都是master&#xff0c;我们计划让7002宕机。 4.4.1.自动故障转移 当集群中有一个master宕机会发生什么呢&#xff1f; 直接停止一个redis实例&#xff0c;例如7002&#xff1a; redis-cli -p 7002 sh…

保持微软Microsoft Teams始终在线的方案

保持微软Microsoft Teams始终在线的方案 背景方案 背景 目前使用微软Teams办公的小伙伴很多&#xff0c;但是长时间不操作电脑就被自动设置成离线状态。对于在电脑前学习书本或者在思考问题的小伙伴就显得不太友好&#xff0c;因为即使我们不操作电脑我们也时刻在电脑前&#…

定时器产生延时停止

1&#xff0c;需求&#xff1a; 当按下按钮SB1,输出信号为0N,指示灯点亮;按下按钮SB2,经过10s的延时后,指示灯熄灭 2&#xff0c;关闭使用定时的常闭触电

HG泄露(ctfhub)

工具准备&#xff1a;dirsearch、dvcs-ripper 网络安全之渗透测试全套工具篇&#xff08;内含安装以及使用方法&#xff09;_dvcs-ripper-CSDN博客 dvcs-ripper&#xff1a;一款perl的版本控制软件信息泄露利用工具&#xff0c;支持bzr、cvs、git、hg、svn... tree //树状…

APP开发_Android 与 js 互相调用

1 js 调用 Android 方法 当使用 JavaScript 调用 Android 原生方法时&#xff0c;主要涉及到 Android 的 WebView 组件&#xff0c;它允许你在 Android 应用中嵌入网页内容&#xff0c;并提供了 JavaScript 与 Android 代码交互的能力。 &#xff08;1&#xff09;创建JavaSc…

项目升级到jdk21后 SpringBoot相关组件的适配

了解到jdk21是一个LTS版本&#xff0c;可以稳定支持协程的功能。经过调研&#xff0c;将目前线上的jdk8升级到21&#xff0c;使用协程提升并发性能。 目前系统使用springBoot 2.0.3.RELEASE&#xff0c;并且引入了mybatis-spring-boot-starter、spring-boot-starter-data-redi…

【电控笔记2.3】速度回路+系统延迟

2.3.1速度回路pi控制器设计 pi伯德图近似设计(不考虑延时理想情况下) Tl:负载转矩 PI控制器的转折频率:Ki/Kp

VScode插件发布

背景 上期在初涉 VS Code 插件开发-CSDN博客中&#xff0c;通过Yeoman工具创建了第一个插件项目&#xff0c;在helloworld的基础上修改&#xff0c;实现预期的功能后&#xff0c;需要将VScode插件发布到插件市场中使用。 官方文档&#xff1a;Publishing Extensions | Visual…

【C语言】带你完全理解指针(六)指针笔试题

目录 1. 2. 3. 4. 5. 6. 7. 8. 1. int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】 2&#xff0c;5 【解析】 定义了一个指向整数的指针ptr&#xff0c;并将其初始化为&…

Angular 使用DomSanitizer防范跨站脚本攻击

跨站脚本Cross-site scripting 简称XSS&#xff0c;是代码注入的一种&#xff0c;是一种网站应用程序的安全漏洞攻击。它允许恶意用户将代码注入到网页上&#xff0c;其他用户在使用网页时就会收到影响&#xff0c;这类攻击通常包含了HTML和用户端脚本语言&#xff08;JS&…

微博百度热搜收集

背景 大家都有使用微博、百度吧&#xff0c;而每天的热搜想必大家也用的不少。微博、百度的热搜有7、8种分类&#xff0c;每个单独查看比较耗费时间&#xff0c;效率极低&#xff0c;大概要花费3&#xff0c;4分钟左右。最近闲来无事&#xff0c;冒出个想法&#xff0c;是不是有…

官宣:2024第二十届国际铸造件展12月精彩呈现!

Shanghai International Die-casting Casting Expo 2024第二十届上海国际压铸、铸造展览会 2024第二十届上海国际压铸、铸件产品展 时间&#xff1a;2024年12月18-20日 地点&#xff1a;上海新国际博览中心&#xff08;浦东区龙阳路2345号&#xff09; 报名参展&#xff1…

【Git】初识 Git

文章目录 1. 提出问题2. 如何解决&#xff1f;版本控制器3. 注意事项 1. 提出问题 不知道你工作或学习时&#xff0c;有没有遇到这样的情况&#xff1a;我们在编写各种文档时&#xff0c;为了防止文档丢失、更改失误、失误后能恢复到原来的版本&#xff0c;不得不复制出一个副…

CC工具箱使用指南:【浙江省村规结构调整表(杨欢)】

一、简介 群友定制工具。 这个工具的功能简单易懂&#xff0c;就是根据输入的现状用地和规划用地图层&#xff0c;生成浙江村规的结构调整表。 村规的结构调整表格式&#xff0c;各个省份都不太一样&#xff0c;无法做一个通用的工具&#xff0c;实在很让人头痛。 看了之后表…

FactoryMethod工厂方法模式详解

目录 模式定义实现方式简单工厂工厂方法主要优点 应用场景源码中的应用 模式定义 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。 Factory Method 使得一个类的实例化延迟到子类。 实现方式 简单工厂 以下示例非设计模式&#xff0c;仅为编码的一种规…

libcurl 简单使用

LibCurl是一个开源的免费的多协议数据传输开源库&#xff0c;该框架具备跨平台性&#xff0c;开源免费&#xff0c;并提供了包括HTTP、FTP、SMTP、POP3等协议的功能&#xff0c;使用libcurl可以方便地进行网络数据传输操作&#xff0c;如发送HTTP请求、下载文件、发送电子邮件等…