JVM体系化学习

2024-04-12 08:28
文章标签 java jvm 学习 体系化

本文主要是介绍JVM体系化学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.JVM基础知识体系化学系

2.JVM拔高学习

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);齐雷 qilei @tedu.cn 1 - 15}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 工具篇:

这篇关于JVM体系化学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/896613

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM