一图流讲解JVM
2020-07-31 14:06:20 0 举报
一图流讲解JVM
作者其他创作
大纲/内容
栈帧1
方法区类被加载到运行时内存时,存放:静态变量、常量、类信息(构造方法、接口定义);比如:Test类public static final Integer i = 666; public static Object object = new Object();方法区和栈比较像,但是区别是方法区存的是类信息,栈存的是方法信息
动态链接
操作数栈
第八步,将栈顶两个int相乘,然后压入栈顶
老年区新生代每一次gc之后存活的对象年龄都会增加1,当达到15的时候,进入到老年代,老年代满了进行full gc,full gc是对整个堆的操作
1
Extension classloader负责加载jar/lib/ext/*.jar中的类
局部变量表
工作内存1,基本数据类型会被分配到工作内存中
第二步,将栈顶int存入变量表,此时a=1
线程... ...
eden区,对象都从这里开始创建
类加载子系统(双亲委派机制和沙箱安全机制)
栈帧... ...
第一步,将int = 1压如栈
Application classloaderclasspath指定的目录或者jar中的类
ireturn 返回,t方法执行完返回到main方法,这时栈帧2需要全部被销毁,现在的栈里只有main一个栈帧了,体现出了后进先出
工作内存... ...
指针
第四步,将栈顶int存入变量表,此时b=2
8:
本地方法栈如果我在main方法中,new Thread().start(),代码跟进去start方法,能看到private native void start0();这就是本地方法接口,会调用C语言的类库,也就是说执行引擎执行到native方法时候,就去调用C语言XX.dll,这些native方法,就会加载到本地方法栈中
第三步,将int = 2压如栈
栈帧2
公共内存
Bootstrap classloader负责加载jre/lib/rt.jar中的类,我们平时用的jdk中的类都位于rt.jar中
老年代(2/3)
栈,先进后出,每一个线程对应一个自己的栈;线程里的每一个方法对应自己所属线程的栈中的一个栈帧。
栈帧中存放的是
区域大小比例:
方法出口
类加载的过程这个过程就成为双亲委派模型,目的就是沙箱保护机制,避免类的重复加载,比如我自己写一个string类,需要被加载时先给app加载器,给ext加载器,走到boot时发现自己已经加载了string,就不会被加载
第九步,将栈顶30压入本地变量
1.需要加载一个类
线程1
程序计数器存放的就是t方法,每行代码前面的这个数字
public class Test { public static final Integer i = 666; public static Object object = new Object(); public int t(){ int a = 1 ; int b = 2 ; int c = (a+b) * 10; return c; } public static void main(String[] args) { Test test = new Test(); System.out.println(test.t()); }}
栈帧1-main方法栈帧与方法是一一对应的,main方法运行时候需要一些资源,t方法运行时候也需要一些资源,这些资源需要隔离开,每个方法都需要自己的资源。代码先执行main方法,为main分配栈帧
命令:java Test.class首先jvm会为当前线程分配
Compiled from \"Test.java\"public class Test { public static final java.lang.Integer i; public static java.lang.Object object; public Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object.\"<init>\":()V 4: return public int t(); Code: 0: iconst_1//将int型1推送至栈顶 1: istore_1//将栈顶int型数值存入第二个本地变量 2: iconst_2//将int型2推送至栈顶 3: istore_2//将栈顶int型数值存入第三个本地变量 4: iload_1//将第二个int型本地变量推送至栈顶 5: iload_2//将第三个int型本地变量推送至栈顶 6: iadd//将栈顶两int型数值相加并将结果压入栈顶 7: bipush 10//将单字节的常量值(-128~127)推送至栈顶 9: imul//将栈顶两int型数值相乘并将结果压入栈顶 10: istore_3//将栈顶int型数值存入第四个本地变量 11: iload_3//将第四个int型本地变量推送至栈顶 12: ireturn//从当前方法返回int public static void main(java.lang.String[]); Code: 0: new #2 // class Test 3: dup 4: invokespecial #3 // Method \"<init>\":()V 7: astore_1 8: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 11: aload_1 12: invokevirtual #5 // Method t:()I 15: invokevirtual #6 // Method java/io/PrintStream.println:(I)V 18: return static {}; Code: 0: sipush 666 3: invokestatic #7 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 6: putstatic #8 // Field i:Ljava/lang/Integer; 9: new #9 // class java/lang/Object 12: dup 13: invokespecial #1 // Method java/lang/Object.\"<init>\":()V 16: putstatic #10 // Field object:Ljava/lang/Object; 19: return}
5.如果也不属于Ext的范畴,继续向下
4.如果不不是jre目录下的(即不属于bootstrap加载器管的范畴)就不会加载,向下把任务派给下级
to suvivor区to区与from区就是互补的关系,也可以互相转换
堆
内存(运行时数据区)
其次通过CPU会启动一个线程,执行这个类
得到
工作内存2
线程2
t方法运行过程
命令:javac Test.java
javap 命令会把字节码文件转换换成可读的JVM字节码文件,这个字节码文件需要参考JVM指令集来进行翻译
放大
2.Application收到加载请求会委托给父加载器
堆用来存放实例对象,GC主要就是针对堆区
math局部变量,这是new出来的,new处理的math对象是存放到堆中的
3.Ext收到请求也不会去加载而是委托给父加载器
1:
第七步,bipush 10,将10压入栈顶
为什么堆中要分新生代和老年代:目前采用的是分代清除算法,新生代存活率低,可以使用复制算法。而老年代对象存活率高,没有额外空间对它进行分配担保,所以只能使用标记清除或者标记整理算法。如何判定一个对象是否应该回收?1.引用计数法:给对象添加一个引用计数器,引用的时候+1,失效-1,计数器为0的对象表示不再被使用;这个方法简单,效率高,但是目前的虚拟机很少选择这个算法管理内存,原因是假如A引用B,B引用A,但是任何其他对象都没有引用A和B,类似这种循环引用的问题,就没有办法了。2.可达性分析算法:定义一个根节点,叫GCRoots,从这个节点开始向下搜索,节点所走过的路径成为引用连,当一个对象到GCRoots没有引用链的话,证明对象可回收
Java栈一个线程对应一个栈,每个方法在执行的时候都会创建一个栈帧来存放该方法运行时需要的局部数据(局部变量表,操作数栈,动态链接,方法出口等)
Test.class
from suvivor区eden区经过一次轻gc之后,存活的对象就会进入到这里
栈帧2-t方法main在运行时,发现需要为t方法分配栈帧,分配之后把栈帧放到栈里(体现先进后出)
新生代(1/3),对象从e区创建,满了之后进行一次轻gc,存活对象存放到f,f满了之后,e+f进行一次gc,存活对象放到t区,清空e+f,使用e+t存放对象,满了之后gc,存活放到f区... ...
第六步,iadd,两个int相加并将结果压入栈顶
0 条评论
回复 删除
下一页