JVM学习笔记

1.JVM数据区域

1.1 类加载器

负责加载Class文件,将Class文件字节码内容加载到内存中。可以理解为快递为Class文件,而类加载器为快递公司。类加载器是有几种的:

  1. 启动类加载器(BootStrap) C++语言编写
  2. 扩展类加载器(Extension) Java语言编写
  3. 应用程序类加载器(AppClassLoader)
  4. 用户自定义加载器,为Java.lang.ClassLoader的子类
    其中1-3为虚拟机自带的加载器
    这三个自带的加载器关系为:BootStrap<–Extension<–AppClassLoader
    Bootstrap里面就是java诞生时所包含的类(rt.jar),例如String类。而随着java版本的扩展,Extension里即为扩展的类(javax.开头的jar包),如List等。

双亲委派机制:当一个类收到类加载器的请求,他首先不会尝试自己来加载这个类,而是委派给自己的父类完成(顺序就是上面写的加载器关系),一路上报。若父类反馈无法加载,则再让子类加载。这样带来了一个好处,即沙箱安全机制。

沙箱安全:假设用户使坏,自己写了一个String类,调用并执行main方法。首先,请求会一路上报至BootStrap加载器,加载器发现同名的String类,这时会报错。这样可以保证源码不会受到污染,保证不同的加载器都最终得到的是同一个对象。

1.2 执行引擎:

我们都知道java会执行.class文件,但是不是只要是后缀为.class的文件虚拟机都会执行,尝试编译了?
答案是不是这样的,当我们打开一个正常的.class文件,发现有这样的规律,文件的开头是cafe babe 后面再跟上字节码。所以并不是所有的.class文件,虚拟机都会进行尝试编译。执行引擎就是验证并执行文件。
当输入字节码,处理过程就是字节码解析的等效过程,输出的是执行结果,即解释.class文件,让操作系统来执行。

1.3 本地库接口、本地方法栈

在java诞生之时,是C/C++流行的时代,作为新生语言的java,不得不要调用、实现一些C/C++的一些功能,而用native 关键字修饰的方法,即表示要调用底层的功能。而调用方法会进栈操作,所以要专门设计一个为本地库接口使用的栈,即为本地方法栈。

1.4 程序计数器

为寄存器。每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

这块内存区域很小,他是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条所需执行的字节码指令。

如果执行的是一个Native方法,那这个计数器是空的。

用以完成分支、循环、跳转、异常处理、线程回复等功能,不会发生OOM错误。

总结:用来储存下一条指令的地址,记录了方法之间的执行情况,也是当前线程所执行的直接骂的行号指示器。

1.5 方法区(可能存在垃圾回收)

供各线程共享的运行时内存区域。存储了每一个类的结构信息,例如运行时常量池、字段、方法数据、构造方法等内容。最典型的就是永久代、元空间。

方法区和堆一样,是各个线程共享的内存区域,他用于存储虚拟机加载的:类信息+普通常量+编译器编译后的代码等。虽然将方法去描述为堆的一个逻辑部分,但它并不是堆的组成部分。

存储类的模板,像类的构造、静态方法、常量

1.6 栈

栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说,不存在垃圾回收问题,只要线程结束该栈就Over,生命周期和线程一直,是线程私有的。

栈执行原理:
一个方法进入栈,入栈后方法和运行期数据的数据集叫做栈帧。当一个方法A被调用时就产生一个栈帧F1,并被亚入到栈中;A方法又调用B方法,于是产生栈帧F2也被压入栈…
执行完毕后,遵循“先进后出”原则

每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完毕的过程,就对应一个栈帧在虚拟机中入栈到出栈的过程。

栈帧主要保存3类数据:

  1. 本地变量:输入参数和输出参数以及方法内的变量
  2. 栈操作:记录出栈、入栈的操作
  3. 栈帧数据:包括类文件、方法等。

1.7 堆

Object object=new ArrayList(),其中等号左边是引用,放在栈中,而等号右边是实例,放在堆中。

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件之后,需要把类、方法、常量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。

堆内存分三部分:新生+养老和永久代

其中永久代不能算是堆的一部分

发生在新生代的GC(垃圾回收)叫做minorGC(复制->清空->互换),发生在养老区的GC叫做FULL GC。

minorGC过程

  1. eden、From复制到To,年龄+1
    当有对象一直被new时,实例对象将会被放在堆中的Eden(伊甸区),当伊甸区满时,会触发第一次GC,杀死大多数对象。将还活着的对象拷贝到From区。当伊甸区再次被填满触发第二次GC时会扫描From和伊甸区,进行垃圾回收。将剩下的对象,拷贝到tTo区(如果有对象的年龄已经到了老年的标准,则赋值到养老区),同时把这些对象的年龄+1
  2. 清空伊甸区,From区,交换
    然后,清空伊甸区和From中的对象,这时,to有对象,From没有对象,发生交换,谁空谁就是To区。
  3. To和From互换
    To和From互换,原To成为下一次GC时的From区。部分对象就会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,参数默认是15),最终如果还是存活,就放入到养老区。

Full GC:
当养老区满了时,会进行Full GC,当执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMEmoryError”。

如果出现java.lang.OutOfMEmoryError:java heap space异常,说明虚拟机的堆内存不够。原因有二:
1.虚拟机的堆内存设置不够,通过参数-Xms、-Xmx来调整。
2.代码中创建了大量大对象,并且长时间不能被垃圾收集器来收集(存在被引用)

1.8 栈+堆+方法区的交互关系

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值