jvm学习

jvm-内存结构和垃圾收集算法

为什么要学习jvm

​ jvm(Java Virtual Machin)即Java虚拟机,它是运行在真实操作性上的虚拟计算机,本质上也是一种程序,真是因为有了它才使得java程序能够运行,我们知道,cpu识别的代码只能是01形式的二进制,所以一般的高级语言都是先翻译成汇编,最终会变成计算机能够识别的二进制执行,java语言也不例外,他的翻译工作是由jvm来做的,为什么不使用计算机系统接翻译java程序呢,这就得说java语言的另一个特性—可移植性

​ 什么是可移植行呢,我们知道,不同的操作系统其cpu的指令集是不一样的,因此要想程序能够在对应系统上运行,那么最终翻译成的二进制指令必须符合其指令集,显然,如果由计算机系统直接翻译,那么我们编写的程序在不同系统上也是不一样的,为了减少成本,提高开发效率,我们迫切需要一种能够屏蔽这种系统差异的东西,这就是jvm,jvm是替代了真实计算机而具有翻译功能的虚拟机,它是由c++编写的软件,在不同的操作系统上其实现也是不同的,因此使得同一java程序可以运行在不同的系统上,这就是java语言的可移植性。

一个java程序的运行流程

​ 我们已经知道,java程序是运行在jvm上的,那么整个java程序从编写到翻译成机器码(应该是先翻译成汇编)的流程是怎么样的呢,接下来我们探讨这个问题。首先,我们最开始编写的程序都是以.java结尾的源文件,这种文件是不能被jvm直接解析执行的,因此需要通过前端编译器(javac)把源文件编译成计算机可识别的.class文件(字节码文件),最终通过类加载器把字节码文件加载到jvm执行,jvm的执行就是把字节码文件翻译成机器码,最终把机器码交给计算机系统的过程,它并不是最终执行程序的地方

image-20201022091110763

jvm内存结构

程序在jvm里面的执行流程

​ 既然jvm如此重要,那么就需要了解其结构以及字节码是怎么在里面执行的。jvm主要包含堆,栈(本地方法栈和虚拟机栈),方法区(逻辑意义,其实现是永久代或者元空间),寄存器和执行引擎。字节码文件主要描述了类信息(方法,属性,修饰符,常量池,父类以及jvm执行指令等信息),当类加载器把字节码文件加载到内存后,这些信息都会存储到方法区,同时在堆区(jdk1.8)生成一个class对象(反射时用到的类对象)。

​ 那么程序又是如何执行的呢,以main方法的执行为例。当我们执行一个main方法时,这时候会开启一个主线程,其对应的class文件会被加载到方法区,同时生成一个class对象,就是执行该对象里面的main方法,所以可以认为方法是程序执行的一个单元,而方法执行说白了就是对数据(局部变量)的操作后最终返回结果,既然涉及到数据和操作两部分,那么就需要有存放数据以及操作的场所,而栈就是充当了这样一个场所。一个方法里面又可能存在方法的层层调用,那么栈又是怎么来组织这样的调用关系的呢,那就是栈里面又分为一个个小块,每一个小块称之为栈帧,每一个栈贞和一个方法对应,而每一个栈贞又细分为局部变量表,操作数栈,动态链接以及返回地址,局部变量表主要存放局部变量,操作数栈主要执行操作,动态链接存放指向方法区运行时常量池里面对应方法的引用,返回地址主要是为了把该方法执行完后下面要执行指令地址返回给寄存器,因为方法会涉及到层层调用,这样会导致寄存器内指令地址发生改变,为了使被调用方法执行完毕后能够找到接下来要执行的地址,所以被调用方法会保存当前调用方法寄存器内地址(下一条要执行指令执的地址),在该方法被调用完后直接把保存的地址返回给寄存器,才能进行后续指令执行,如果发生异常,会根据异常表来确定,不会返回任何东西。在这个过程中,利用了栈的先进后出特性,和方法的层层调用相对应,而执行引擎相当于一个磁头的作用,需要知道对应指令地址才能推动着指令往下执行。同时生成对应的机器码。

例如,main()->test1()->test()2

image-20201022114405619

jvm内存结构各部分存储的是什么

image-20201022115402619

方法区:也称非堆,是一个逻辑分区,永久代(jdk1.8之前)和元空间是其真实实现,元空间用的是物理机内存。主要存放class文件信息,jit代码缓存,运行时常量池,

虚拟机栈:存放局部变量和引用,理论上在对象未逃逸情况下可以在栈上分配。

本地方法机栈:存放调用本地方法时需要的东西。

堆:存放class对象(1.8),new出来的对象,数组对象,静态变量(因为属于class对象)和字符串常量池(jdk1.7时才放到了堆区)。

程序计数器:存放下一条要执行的字节码指令地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKpGxs5S-1640514778977)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20201022144110190.png)]

image-20201022144043924

image-20201022144136888

垃圾回收

会什么要进行垃圾回收

​ jvm内存(运行时数据区)是字节码指令处理的场所,在这个过程中会产生大量对象和加载很多class文件,如果不及时回收,将会撑爆内存,导致程序崩溃,所以要进行垃圾回收。

谁来回收以及怎么回收

​ jvm维护了一个后台GC线程,当需要垃圾回收时由执行引擎唤醒GC线程去调用垃圾回收器回收垃圾,具体怎么回收有相应的回收算法以及寻找垃圾的算法。

有哪些寻找垃圾的算法

引用计数法

​ 引用计数法是指每个对象有个引用计数器,被引用时计数器加1,引用失效时减一,该值为0则对象可以回收。

​ 优点:实现简单,没有stw(让用户线程停止)

​ 缺点:引用计数器会造成资源浪费,最重要的是两个相互引用的对象无法回收,从而造成内存泄漏。

image-20201022124412093

可达性分析法(默认使用

​ 从GC ROOTS开始依次往下搜索,最终形成一颗数链,会把链上的对象进行标记,最终那些与GC ROOTS上的任意对象没有联系的对象则没有标记,会被当做垃圾对象回收。

优点:不会存在相互引用的垃圾对象无法被回收的情况

缺点:在分析时需要停止所有用户线程(stw)。

image-20201022133958162

有哪些清理垃圾的算法

标记清楚算法

标记清楚算法分为两个阶段:

第一阶段标记:找出哪些是垃圾对象进行标记。

image-20201022135548190

第一阶段标记:找出哪些是被标记的对象进行清除。

image-20201022135851540

优点:实现简单。

缺点:会产生内存碎片,在创建大对象时容易出现内存不够从而导致新的gc,使用空闲列表的方式创建对象。

复制算法

​ 需要两块内存空间,当找到应该存活的对象时把其从该空间直接复制到另一块空间,然后把该空间清理,如此往复进行。

image-20201022141430362

image-20201022141937668

优点:直接复制,效率高,不会产生垃圾碎片,可使用指针碰撞的方式创建新对象。

缺点:有一半的空间未被使用。

标记压缩算法(标记整理算法

标记压缩算法分为两个阶段:

标记:标记处所有的垃圾对象。

整理:将活着的对象向一端移动,清除垃圾对象。

image-20201022143156748

优点:避免了复制算法空间减半和标记清除算法内存碎片化问题。

缺点:执行效率低于复制算法,因为需要标记。如果移动对象被引用,需要调整引用地址,同时移动过程中需要stw。

堆内存的结构以及使用的垃圾回收算法

分代结构

​ 分代结构是整个堆可划分为新生代和老年代两个区域,默认大小比例为1:2,而新生代又分为Eden(伊甸园区)和Survivor区,Survivor区分为Survivor0和Survivor1区,或者是from和to区。

image-20201022145329917

​ 新生代的结构很符合复制算法的要求,当然各新生代的垃圾收集器也是采用了复制算法,所有的新对象都是在Eden区内生成,当Eden区满了后就会触发minor gc,所谓minor gc就是指只对新生代进行垃圾回收,其具体过程是,第一次把Eden区内存活对象复制到from区,如果Eden区再满,进行第二次minor gc,此时会把Eden区和from区内的存活对象都复制到to区,此时from区和to区进行角色交换,from区会变成to区,也就是说谁是空的谁就是to区,如此往复,每个对象都会有一个计数器,每次存活后计数器加一,当达到15(默认值)后该对象如果还存活就会进入老年区。

​ 在老年代,一般的垃圾收集器都是采用了标记整理算法,也有标记清楚算法(cms),同时只进行老年代的清理称之为major jc,其stw时间比minor gc要长,当然stw时间最长的是full gc,因为它是对整个堆空间的清理,三种gc的触发条件如下:

Minor GC:从年轻代回收内存
触发条件
1、Eden区域满

2、新创建的对象大小 > Eden所剩空间

Full GC:清理整个堆空间,包括年轻代和老年代
触发条件
​ 1、每次晋升到老年代的对象平均大小>老年代剩余空间

2、MinorGC后存活的对象超过了老年代剩余空间

3、方法区空间不足

4、System.gc()()(不一定触发)

5、堆内存分配很大的对象

Major GC:清理老年代

1、老年代的使用空间达到某个阈值,JVM的默认值是92%(jdk1.5之前是68%,jdk1.6之后是92%)

分区结构

​ 分区结构是把堆划分成一个个小块,当然也有Eden,Survivor和old这叫个角色,另外有存放大对象的角色。

image-20201022153837033

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值