JVM虚拟机执行原理

        JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

         引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

首先给大家看下jdk体系结构图:

JDK:除了包含JRE(运行时环境)和JVM(虚拟机)外,还包含了一系列Tools(java命令):javac、javadoc等

JRE:常用的一些类库

JVM:虚拟机

我们刚开始接触java的时候,写的第一个java代码就是HelleoWorld.java,底层大概是这样运行的:

我们在下载JDK时都会下载对应版本:Windows版本,Linux版本,Mac版本

接下来我们结合一个简单的程序来具体讲下JVM:

1,先写一个简单程序:

public class Math {

    public static Integer CONSTANF = 666;

    public int compute(){
        int a = 1;
        int b = 2;
        int c =(a+b)*10;
        return c;
    }
    public static void main(String[] args) {
        Math math = new Math();
        math.compute();
    }
}

2,该程序在JVM中的执行流程:

(1)栈(线程):每个线程执行时,虚拟机都会开放一个空间存储当前线程的栈。如上图所示,栈主要存储每个方法中的局部变量(如果是引用类型变量,存的是指向堆中对象的指针地址),每个方法有对应的栈帧。该栈(线程)又满足栈数据结构特点,main方法先进来,后被销毁。栈帧又包括:局部变量表、操作数栈、动态链接、方法出口。

局部变量表:存储a、b、c

操作数栈:临时存储1、2、3

动态链接:存放着compute()方法对应的JVM指令码的内存地址(程序运行时动态生成的)

方法出口:程序计数器记录的math.compute()方法处于哪一行,compute()方法执行结束后回到该行

(2)本地方法栈:由native修饰的方法(底层由C语音实现),用于java和C语言的交互

(3)程序计数器:用于记录程序具体执行到哪一行代码,对改行代码给个编号标记

(4)执行引擎:执行程序中的代码

(5)方法区(元空间:1.8之后使用的是直接内存,没有用JVM内存):Math.class类元信息(常量、静态变量、方法等类元信息)

(6)类装载子系统:将.class文件加载到运行时数据区

(7)堆:我们new出来的对象存储在堆中。堆分为年轻代和老年代。

a,年轻代(占整个空间三分之二):分为Eden区(默认占年轻代十分之八)和Survivor区(默认占年轻代十分之二)

b,老年代(占整个空间三分之一)

我们new()出来的对象一开始放在Eden区的,当Eden区放满的时候,触发执行引擎发生minor gc(年轻代垃圾回收),会处理掉一些没有被引用的对象(当方法执行完栈帧会别释放,也就是存放指向堆中对象的地址变量也释放了,也叫GCRoots根释放了)。经过一次minor gc后,有用的对象会往Survivor中from区(对应的分代年龄+1),再满了之后from区就往TO区整个复制,就在from和to之间来回复制(垃圾回收用到很多算法:标记-清除算法、标记-压缩算法、复制算法、分代收集算法),当对象的分代年龄达到15次之后,对象就会移到老年代,当老年代放满的时候就会发生full gc(对老年代里的对象做垃圾回收),如果发现老年代里的对象是有被引用的,程序崩溃,发生内存溢出。当发生full gc时,就会停止所有应用线程(stop world),JVM调优就是减少发生full gc次数

3,需要GC的内存区域

          jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。

4,GC的对象

需要进行回收的对象就是已经没有存活的对象,判断一个对象是否存活常用的有两种办法:引用计数和可达分析。

(1)引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

(2)可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

5,GC过程

分代收集算法:

现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代(Young)和老年代(Tenure)。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。

(1)当系统创建一个对象的时候,总是在Eden区操作,当这个区满了,那么就会触发一次YoungGC,也就是年轻代的垃圾回收。一般来说这时候不是所有的对象都没用了,所以就会把还能用的对象复制到From区。

(2)这样整个Eden区就被清理干净了,可以继续创建新的对象,当Eden区再次被用完,就再触发一次YoungGC,然后呢,注意,这个时候跟刚才稍稍有点区别。这次触发YoungGC后,会将Eden区与From区还在被使用的对象复制到To区

(3)再下一次YoungGC的时候,则是将Eden区与To区中的还在被使用的对象复制到From区

(4)经过15次YoungGC后,有些对象在From与To之间来回游荡,这时候From区与To区亮出了底线(阈值),这些家伙要是到现在还没挂掉,对不起,一起滚到(复制)老年代吧。 

老年代经过这么几次折腾,也就扛不住了(空间被用完),好,那就来次集体大扫除(Full GC),也就是全量回收。如果Full GC使用太频繁的话,无疑会对系统性能产生很大的影响。所以根据业务场景合理设置年轻代与老年代的大小,尽量减少Full GC的操作。

6,GC调优思路:

JVM调优主要就是为了减少full gc 次数(当老年代空间存储满了就会触发full gc),所以呢尽量让垃圾对象在年轻代就被清除
1、打印GC日志
-XX:+PrintGCDetails  -XX:+PrintGCTimeStamps  -XX:+PrintGCDateStamps  -Xloggc:./gc.log
Tomcat则直接加在JAVA_OPTS变量里
2、分析日志得到关键性指标
3、分析GC原因,调优JVM参数
 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值