JVM蓝图
2020-08-21 09:30:45 21 举报
深入JVM各个区域
作者其他创作
大纲/内容
使用
先通过可达性分析标记有效对象,然后将垃圾对象的内存空间用后面的有效对象进行填充。优点:堆内存不会产生碎片缺点:效率较低
4.重复上面的3,其中S1换成S2,还需要注意的是每次GC存活的对象年龄都会加1
Edan区
可达性分析:根据JVM中GCRoots找到所有的有效对象标记,遍历堆内存其他未被标记的对象即为垃圾
7.本地方法栈和虚拟机栈
标记-清理
可达性分析算法
堆是JVM中占用内存最大的一块区域,该区域主要来存放new的实例对象,几乎所用创建的实例对象都在这里分配空间。由于堆区域的对象较多,可能经常需要进行垃圾回收,清理掉那些没有引用指向的对象。
新生代中的复制算法(粗略步骤)
方法区(非堆,元数据)
0
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class对象, Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误
永久代和元空间都是方法区的具体实现。1.永久代: Jdk 1.7 中,永久代存放了一些类的元信息,动态加载类时是可能发生JVM内存溢出的。jdk1.7将字面量,类的静态变量这些都移入了堆中。2.元空间:到了jdk1.8 ,永久代被彻底移除了,替换他的是元空间,元空间使用的本地内存而不是JVM的内存,所以元空间的大小取决于本地内存,当让也可以设置虚拟机参数。3.方法区只是一个逻辑空间,并非物理空间。(可能物理内存接在堆后面)4.永久代缺点: 1.由于永久代内存经常不够用或者发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen 。 字符串存在永久代中,容易出现性能问题和内存溢出。 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢 出,太大则容易导致老年代溢出。 2、永久代会位GC带来不必要的复杂度,而且回收效率偏低。
S2
Bootstrap ClassLoader
1.什么是垃圾?没有GC Roots上的引用指向该对象了。2.GC Roots引用指的是哪些?方法区中常量池中的引用,VMstack中局部变量表中的引用,nativestack中JNI的引用
局部变量表
本地接口
1
复制算法:将堆内存划分成两部分,然后通过可达性分析标记内存中有效对象,将有效对象复制到另外一半内存空间,然后将这块内存空间清空。缺点:内存大小被减半,如果对象多存在较多的复制
堆
JVM中堆算法的具体
GC
1.新进来的对象会先进入Edan区 , 该区域满时进行第一次GC
它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
有效对象
垃圾回收算法
6.当老年代放不下时也会进行GC(标记整理),GC任然放不下可能会发生OOM
卸载
JVM内存结构
Class文件
用于存放一些临时的操作数据
方法出口
1.加载: 该过程是将字节码文件加载到方法区,并在堆中创建 java.lang.Class对象作为方法区信息的访问入口。 2.连接(验证 - > 准备 -> 解析) 验证:对字节码文件正误性进行验证 span style=\"font-size: inherit;\
java源代码
javac
3.之后新进来的对象放入S2和Edan区,放满时再进行GC,将Edan区和S2区对象放入S1,清空Edan和S2.
执行引擎
运行时内存环境
虚拟机栈
当我们通过point.method1调用该方法时实际是要加载方法区类信息,这个过程也就是动态链接将方法区指令地址动态加载于栈中
存放方法里面的局部变量,方法返回后将会销毁
垃圾
复制
。。。。。。
JVM运行时内存:线程私有的: 程序计数器:1.记录线程下一条指令的执行位置(多线程时,cpu线程切换所有需要保存) 2.每个线程都有自己私有的程序计数器。 3.JVM层面上的一个抽象,记录了字节码指令地址,当执行native方法时, 是未定义的,因为native是c/c++方法非java字节码实现。 4.所占用内存空间小,为一个不存在OOM的地方。 虚拟机栈: 1.Java方法执行的地方 2.每调用一个方法时会分配栈帧 3.栈帧包含:局部变量表,操作数栈,动态链接,方法出口。 4.当虚拟机栈大小固定时,可能发生StackOverflowError,当虚拟机栈可扩展时可 能发生OOM。 本地方法栈:1.执行本地方法的地方(和JVM栈类似) 2.HotSpot将JVM栈和本地方法栈合并了。线程共享的: 方法区(非堆):1.用于存放一些个类信息,常量,静态变量的地方 2.由于该区域的信息,存放时间比较长(该区域也叫永久代元空间),所以这块 区域垃圾回收的较少(主要是类卸载或常量的回收) 3.当类信息过多时该区域可能发生OOM。 堆:1.与非堆相反,该区域主要用于存放,实例对象的(new 的对象)。 2.由于该区域对象的生命周期或长或短,该区域可能经常需要垃圾回收。 3.该区域根据对象的存活长度,划分成了新生代(edan, survivor)和老年区。 4.该区域不可在扩展时可能发生OOM。
4.类加载器
堆根据对象生存时间被划分成了两块区域,新生代和老年代。这两块区域垃圾回收算法也是不同的。
引用计数算法
GC Roots
初始化
本地方法栈
2.第一次GC将Edan区有效对象全部复制到幸存区2,并清空该区域
3.类的生命周期
类加载器
5.堆
栈
它负责加载 JDK\\jre\\lib\\ext目录中,或者由 java.ext.dirs系统变量指定的路径中的所有类库(如javax.开头的类),开发者可以直接使用扩展类加载器。
私有
程序计数器
AppClassLoader
动态链接
根据堆中对象的生命周期将堆区划分分成了 新生代 和 老年代。1.新生代 : 对象存活时间不长 , GC经常发生。(该区域存活足够长时间的对象会放入老 年代)该区域适合使用复制算法。2.老年代: 对象存活时间较长,GC发生少。(如Class对象会放入其中)该区域适合使用标 记整理算法。
1.java代码的编译成字节码
方法的返回地址
解析
注意:这三个类加载 器是组合的方式而非继承
站在Java虚拟机的角度来讲,只存在两种不同的类加载器:启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有很多其他的虚拟机是用Java语言实现的),是虚拟机自身的一部分;所有其它的类加载器:这些类加载器都由Java语言实现,独立于虚拟机之外,并且全部继承自抽象类 java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类
调用
类加载过程
本地库
加载
2.类加载
1.本地方法栈是用来执行本地方法的,虚拟加栈是用来执行java方法的。(有些JVM实现将本地方法栈也放入了虚拟机栈中去了)。2.栈里面含有栈帧,如果设置栈的大小过大可能会减少栈的数目,因为栈这个内存区域大小固定,一个栈的大小太大,总的栈数会减少,如果栈的大小过小可能发生栈溢出。当然当无法申请到栈时可能发生OOM。
6.关于永久代和元空间
5.直到S1或S2放不下时,将那些年龄超过一定值的对象放入老年代
1.java源代码会通过 javac.exe (编译器) 译成jvm所能够理解的字节码文件(class文件)2.字节码文件的好处在于平台的无关性,JVM在不同平台可以翻译成适应不同平台cpu和os的机器码
S1
双亲委派模型源码部分
顾名思义,先通过可达性分析标记有效对象,然后扫描堆内存回收垃圾。缺点:堆内会产生很多碎片优点:效率较高
标记-整理
操作数栈
验证
extClassLoader
准备
JVM类加载机制:1.全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入2.父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类3.缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
共享
JVM内存结构抽象全局图
栈帧
1.启动类加载器由c/c++实现2.负责加载存放在 JDK\\jre\\lib(JDK代表JDK的安装目录,下同)下3.启动类加载器是无法被Java程序直接引用的。
老年代
引用计数算法:通过给堆内存中每个对象增加一个计数器,即有引用指向他时,计数器加一,计数器的值为0时被视为垃圾。此方式存在一个问题是,如上左图,堆内部对象循环依赖时对象的计数器永远无法为0.
具体的垃圾回收算法
Class.forName():将字节码文件加载到JVM并执行static语句块(也可以不执行static 通过传入参数控制)ClassLoader.loadClass():仅仅将字节码加载到JVM中,不执行static块
0 条评论
下一页