课堂笔记:JVM(一)运行时的数据区详解

JVM

  • JVM内存结构
  • 类加载机制和JDK的调优命令工具
  • GC格式日志
  • GC调优实践

java虚拟机(java virtual mache,JVM),一种能够运行java字节码的虚拟机。作为一种编程语言的虚拟机,实际上不只是专用于java语言,只要生成的编译文件匹配对加载编译文件格式要求,任何语言都可以由JVM编译运行。比如kotlin、scala等。

JVM还有很多,不只是hotspot,还有JRockit、J9等等。

一、JVM的基本结构

JVM主要由三个主要的子系统构成。

  • 类加载子系统
  • 运行时数据区(内存结构)
  • 执行引擎

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeiHcRRQ-1590650083873)(C:\Users\tuoji\AppData\Roaming\Typora\typora-user-images\image-20200528104546412.png)]
在这里插入图片描述

我们首先写一个简单的程序:

public class Demo1 {
    public int math() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        int math = demo1.math();
    }
}

然后在cmd窗口下,使用“javac Demo1.java”将其进行编译处理得到一个class文件,随后使用“sublim”打开该文件:

cafe babe 0000 0034 0014 0a00 0500 1007
0011 0a00 0200 100a 0002 0012 0700 1301
0006 3c69 6e69 743e 0100 0328 2956 0100
0443 6f64 6501 000f 4c69 6e65 4e75 6d62
6572 5461 626c 6501 0004 6d61 7468 0100
0328 2949 0100 046d 6169 6e01 0016 285b
4c6a 6176 612f 6c61 6e67 2f53 7472 696e
673b 2956 0100 0a53 6f75 7263 6546 696c
6501 000a 4465 6d6f 312e 6a61 7661 0c00
0600 0701 000f 636f 6d2f 6c75 6261 6e2f
4465 6d6f 310c 000a 000b 0100 106a 6176
612f 6c61 6e67 2f4f 626a 6563 7400 2100
0200 0500 0000 0000 0300 0100 0600 0700
0100 0800 0000 1d00 0100 0100 0000 052a
b700 01b1 0000 0001 0009 0000 0006 0001
0000 0003 0001 000a 000b 0001 0008 0000
0031 0002 0004 0000 000d 043c 053d 1b1c
6010 0a68 3e1d ac00 0000 0100 0900 0000
1200 0400 0000 0500 0200 0600 0400 0700
0b00 0800 0900 0c00 0d00 0100 0800 0000
2e00 0200 0300 0000 0ebb 0002 59b7 0003
4c2b b600 043d b100 0000 0100 0900 0000
0e00 0300 0000 0c00 0800 0d00 0d00 0e00
0100 0e00 0000 0200 0f

这里的“cafe babe”被称为魔数。

然后我们在虚拟机栈中分析该类的执行过程。

方法的调用过程就是栈帧的入栈出栈操作。

随后我们使用“javap -c Demo1.class > demo.txt” 将生成的class文件进行反汇编到一个demo文件中:

Compiled from "Demo1.java"
public class com.luban.Demo1 {
  public com.luban.Demo1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int math();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/luban/Demo1
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method math:()I
      12: istore_2
      13: return
}

虚拟机栈是线程私有的,每一个方法都会放入到栈帧中去执行。方法的执行就是栈帧的入栈与出栈。而且虚拟机栈中没有垃圾回收,因为线程执行完方法后会销毁,内存会自动被回收释放。

在这里插入图片描述

1、虚拟机栈

  • 局部变量表:方法局部变量。
  • 操作数栈:获取方法中的变量和执行计算,最终结果会记录到局部变量表中。计算的时候每次取栈顶的两个元素。
  • 方法出口:方法结束分为正常退出和异常退出。方法出口保存了一个调用该方法的内存地址,指向了调用该方法的栈帧。
  • 动态链接:
public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        Map<String, String> map = new HashMap<>();
        map.put("test", "test");
        int math = demo1.math();
    }

在这里插入图片描述

这里我们创建的接口的map,这个map会连接到堆中创建的HashMap的实例,在调用put方法的时候其实会调用HashMap的方法。

2、本地方法栈

执行引擎------------本地方法接口------native方法库

和栈作用很相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行native方法服务。登记native方法,在Execution Engine执行时加载本地方法库

3、程序计数器

是一个指针,指向方法的字节码,存储下一条指令的地址。由执行引擎读取到下一条指令。

就是一个指针,指向方法区中的方法字节码(用来存储指向吓一跳指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

4、方法区

jdk1.8之前对方法区的实现是hotspot永久代、JRocket元空间。在JDK1.8之后,只有元空间。区别是:元空间使用的是本地物理内存,之前是放在堆中的。

类的所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在这里定义。简单来说,所有定义的方法的信息都保存在该区域,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是为了和Java的堆区分开(jdk1.8以前hotspot虚拟机叫永久代、持久代,jdk1.8时叫元空间)

方法区和堆一样,都是线程共享的内存区域,被用于存储已被虚拟机加载的类信息(字段等)、即时编译后的代码(方法字节码)、静态变量和常量等数据。

方法区也会发生GC
根据Java虚拟机规范的规定,方法区无法满足内存分配需求时,也会抛出OutOfMemoryError异常,虽然规范规定虚拟机可以不实现垃圾收集,因为和堆的垃圾回收效率相比,方法区的回收效率实在太低,但是此部分内存区域也是可以被回收的。

方法区的垃圾回收主要有两种,分别是对废弃常量的回收(常量池的回收)和对无用类的回收(类的卸载)。

当一个常量对象不再任何地方被引用的时候,则被标记为废弃常量,这个常量可以被回收。

5、堆(Heap

虚拟机启动时自动分配创建,用于存放对象的实例,几乎所有对象都在堆上分配内存,当对象无法在该空间申请到内存是将抛出OutOfMemoryError异常。同时也是垃圾收集器管理的主要区域。

在这里插入图片描述

新生代和老年代各占堆的1/3和2/3.而在新声代中,Eden区和Survivor占比为8:1.

5.1 新生代(Young Generation)

类出生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,

结束生命。

新生代分为两部分:伊甸区(Eden space)和幸存者区(Survivor space),所有的类都是在伊甸区被new出来的。幸存区又分为From和To区。当Eden区的空间用完是,程序又需要创建对象,JVM的垃圾回收器将Eden区进行垃圾回收(Minor GC),将Eden区中的不再被其它对象应用的对象进行销毁。然后将Eden区中剩余的对象移到From Survivor区。若From Survivor区也满了,再对该区进行垃圾回收,然后移动到To Survivor区。

5.2 老年代(Old Generation)

新生代经过多次GC仍然存货的对象移动到老年区。若老年代也满了,这时候将发生Major GC(也可以叫Full GC),进行老年区的内存清理。若老年区执行了Full GC之后发现依然无法进行对象的保存,就会抛出OOM(OutOfMemoryError)异常

5.3 元空间(Meta Space)

在JDK1.8之后,元空间替代了永久代,它是对JVM规范中方法区的实现,区别在于元数据区不在虚拟机当中,而是用的本地内存,永久代在虚拟机当中,永久代逻辑结构上也属于堆,但是物理上不属于。

为什么移除了永久代

参考官方解释http://openjdk.java.net/jeps/122

大概意思是移除永久代是为融合HotSpot与 JRockit而做出的努力,因为JRockit没有永久代,不需要配置永久代。

在虚拟机当中,永久代逻辑结构上也属于堆,但是物理上不属于。

为什么移除了永久代

参考官方解释http://openjdk.java.net/jeps/122

大概意思是移除永久代是为融合HotSpot与 JRockit而做出的努力,因为JRockit没有永久代,不需要配置永久代。

在这里插入图片描述

补充:GC详解

了解java虚拟机堆内存的分配策略

堆内存: 新生代(1个eden+2个survior)+老生代
New的对象首先进入eden中,经历一次minorGC后,如果对象还在则进入survior区域;其后每经过一次minorGC则会存活次数增加一个,到达一定的程度后进入老生代块中;
minorGC:对新生代堆块进行垃圾回收,因为此时大多数的对象都是朝生夕死,所以使用复制算法;
fullGC:对老生代区回收,因为死去的对象较少,所以使用标记整理算法.

新生代中为甚是两个survior

理解新生代的1个eden和2个survior,为什么是两个survior?假设两个survior分别是s1和s2
那么第一次minorGC:s1和s2都是空的,eden中有值,gc时eden中的存活对象会复制到s1;第二次gc,此时eden中的存活对象复制到s2,这些对象年龄统一为1,然后s1中的存活对象会将年龄加1,然后判断年龄是否足够大,足够大的进入老生代,别的进入s2,并且清空s1和eden;随后的gc与第二次gc相似;
从上面能够看出,eden和survior在minorGC时操作存在不同。

触发GC时机(什么时候)

minorGC触发:eden已满或者新建的对象已经没有空间可存
fullGC:minorGC之前,比较survior区域对象的大小和老生代剩余连续内存的大小,如果fullGC剩余连续空间小于survior区域对象,会查询HandlePromotionFailure设置是否允许担保失败,如果不允许则直接进行进行fullGC,否则继续比较老生代剩余连续内存空间和历次晋升到老年代去的对象的平均大小,如果小于,则进行fullGC,否则尝试minorGC,如果失败也会fullGC;

被GC的对象(对什么东西)

如果对象到GC ROOTS 没有连接链,则该对象为不可达对象,应该被GC回收.

GC过程(做了什么事)

MinorGC:使用复制算法,清除新生代中的不可达对象.一个eden区域,两个survivor区域,仅使用eden和一个survior区域,minorGC时,把eden和已使用survior存活对象放入另一个survior中,然后清理刚刚使用的eden和survior
FullGC:清理整个堆空间包括新生代和老年代,老年代使用标记整理算法,清除老生代中的不可达对象.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值