JVM体系结构详解

Posted 未来首席架构师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM体系结构详解相关的知识,希望对你有一定的参考价值。

一、JVM整体架构细节


 

二、类加载器

2.1、类加载器结构与过程:


 

2.2、类加载器分类

1、引导类加载器——虚拟机自带,属于启动类加载器

2、扩展类加载器

3、系统类加载器

 

2.3、双亲委派机制

1、类似设计模式的职责链模式,让父类创建对象,父类创建不了的情况下自己再创建

2、优点:避免类的重复加载、保护程序安全,防止核心API被随意篡改

 

2.4、沙箱安全机制

自定义String类,加载时,率先使用引导类加载器,而引导类加载器在加载过程中会先加载JDK自带的文件,保证对java核心源代码的保护

 

2.5、JVM中两个class对象是否为同一个类存在两个必要条件

 

2.6、类的使用分为主动使用和被动使用


三、运行时数据区

3.1 PC Register

1)概述

1、存储指令相关的现场信息

2、非物理寄存器,抽象模拟

3、运行速度最快的存储区域,占用空间很小

4、每个线程都有自己的程序计数器,生命周期一致

5、唯一一个在java虚拟机规范中没有规定任何OOMERROR情况的区域

2)寄存器私有线程

1、线程之间独立计算,不会出现相互干扰的情况

 

3)CPU时间片

1CPU分配给各个程序的时间,每个线程被分配一个时间段

2、程序被轮流执行


3.2 虚拟机栈

1)概述

1、为了跨平台,解决不同CPU架构兼容性问题,所以摒弃寄存器,采用栈结构

2、指令集小,编译容易实现,性能下降,实现同样的功能需要更多的指令

3、栈是运行时单位,堆是存储单位

4、每个线程创建时都会创建一个虚拟机栈,内部保存一个个的栈帧,对应着一次次的java方法调用

5、生命周期和线程一致

6、作用:主管java程序的运行,保存方法的局部变量、部分结果,参与方法的调用和返回

7JVM操作java栈的仅有的两个操作:方法执行——>进栈;执行结束——>出栈

8、对于栈没有垃圾回收的概念[进出栈,无垃圾]

9、常见异常:固定大小的虚拟机栈溢出SOFE;可以动态扩展但是无法申请到足够的内存OOME

10、设置占内存大小:-Xss

 

2)栈的存储单位

1、栈帧格式存储

2、当前栈帧、当前方法、当前类

3、不同线程之间的栈帧不允许相互引用

4、每个栈帧中的存储结构

  1. 局部变量表

  2. 操作数栈

  3. 动态链接

  4. 一些附加信息

 

3)局部变量表

1、最基本的单元是slot(变量槽)

232位以内类型只占用一个slot64位占用两个slot

3JVM会为每一个slot分配一个访问索引

4、可以重用

5、局部变量必须认为初始化,否则不可用

6、局部变量表中的变量是重要的垃圾回收根节点

 

4)操作数栈[表达式栈]

1、后进先出

2、保存计算过程中的中间结果,变量的临时存储空间

3、栈深度是确定的

432位一个栈深度,64位两个

5、无索引,标准出入栈

 

5)栈顶缓存技术

栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读写次数,提升执行引擎的执行效率

 

6)动态链接

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接


 

7)方法的调用

1、方法的绑定机制:早期绑定和晚期绑定

2、虚方法与非虚方法

3lambda的引用使java具备了动态语言的特性

 

1、一个方法的结束:正常完成;异常退出

2、都返回到该方法被调用的位置,异常时通过异常表来确定返回值

 

9)一些附加信息

程序调试提供支持的信息


3.3本地方法栈和接口

1)本地方法接口是调用底层方法

 

2)本地方法栈概述

1java虚拟机栈管理java方法调用;本地方法栈管理本地方法的调用

2、线程私有

3、大小设置与JAVA虚拟机栈相同

4、本地方法栈是使用C语言实现的

5、当某个方法调用一个本地方法时,其就进入了一个全新的并且不受虚拟机限制的世界

6hotspotJVM直接将本地方法栈和虚拟机栈合二为一


四、堆

4.1 堆的核心概述

1、一个JVM实例只存在一个堆内存,是JAVA内存管理的核心区域

2、可通过参数调节

3、物理上可以不连续,逻辑上是连续的

4、所有线程共享java

5、所有的对象实例及数组都应当在运行时分配在堆上

6、数组和对象永远不会存储在栈上

7、方法结束后,堆中的对象不会马上被移除,有专门的的回收机制处理

8Java 7 堆内存逻辑分三部分:新生去、养老区[tenure]、永久区

9JAVA8及以后内存逻辑分三部分:新生去、养老区、元空间


 

4.2 设置堆内存大小与OOM

详见参数设置篇

 

4.3 年轻代与老年代

1Eden:s0:s1=8:1:1


 

4.4 图解对象分配过程

垃圾回收频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间手机

 

4.5 MinorGCMajorGCFullGC

1MinorGC

  • 只是新生代的垃圾收集;

  • 会引发STW,暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行

2MajorGC

  • 只是老年代的垃圾收集;

  • MinorGC速度慢10倍以上,STW的时间更长;

  • GC后内存还不足就报OOM

3FullGC

收集整个java堆和方法区的垃圾收集

触发情况:

System.gc(),系统建议执行

老年代空间不足

方法区空间不足

MajorGC后,进入老年代的平均大小大于老年代的可用内存

4Mixed GC:收集整个新生代以及部分老年代的垃圾收集


 

4.6 堆空间分代思想

170%~99%的对象时临时对象

2、分代的唯一理由:优化GC性能

 

4.7 内存分配策略

1、如果对象在Eden出生并经过第一次MGC后,仍然存货,并且被S容纳,则对象年龄++,当年龄增加到15时,就会晋升到老年代

2、优先分配Eden

3、大对象直接分配到老年代——字符串、数组

4、长期存活的对象分配到老年代

5、动态对象年龄判断:空间和Threshold的关系

 

4.8 为对象分配内存:TLAB

2JVM为每个线程分配了一个私有缓存区域,包含在Eden空间内

3、避免非线程安全问题

4TLAB空间内存非常小,仅是Eden1%,可配置分配

5TLAB分配失败后,采用加锁机制在Eden空间中直接分配,并采用加锁机制确保线程安全

 

4.9 小结堆空间的参数设置

详见参数设置篇

 

 4.10 堆事分配对象的唯一选择吗

如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配

 

4.11 逃逸分析

1、通过逃逸分析,JAVA Hotspot编译器能够分析出一个新的对象的引用的适用范围从而决定是否要将这个对象分配到堆上

2、基本行为就是分析对象动态作用域

当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸

否则发生逃逸

3JIT编译器针对没有逃逸出方法的对象就用栈上分配,用完即回收,不浪费空间


五、方法区

5.1 栈、堆、方法区的交互关系


5.2 方法区的理解

1、各个线程共享的内存区域

2、可固定大小或者可扩展

3、方法区大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误OOM

4、元空间不在虚拟机设置的内存中,使用本地内存

 

5.3 设置方法区大小与OOM

详见参数设置篇

 

5.4 方法区的内部结构

1、它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等

2、类型信息:classinterfaceenum。。。。。。。

3、域信息:publicprivate。。。。。。

4、方法信息:voidnativeabstarctfinal。。。。。。。。

5、全局常量:static final

6、为什么需要常量池

java的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换一种方式,可以存到常量池,这个字节码包含了指向常量池的引用

常量池内容:数量值、字符串值、类引用、字段引用、方法引用


以上是关于JVM体系结构详解的主要内容,如果未能解决你的问题,请参考以下文章

JVM虚拟机详解JVM与JAVA体系统结构

JVM整体结构-java栈详解

详解Jvm内存结构

详解Jvm内存结构

详解Jvm内存结构

JVM体系结构详解