JVM
JVM的基本结构
首先是一个Java文件通过Javac编译成class文件,然后经过Lorder进入到类加载子系统
类加载系统负责对类的加载,也包含着类加载的流程——加载,验证,准备,解析,初始化
目的就是将本地的或者网络上的字节码文件加载到JVM中,转化为Class信息
方法区,是一个规范,根据JDK的版本不同,又分为永久代和元空间,负责存储加载进来的类信息,所有线程都是共享的
Java堆,几乎所有的数组,对象,都是存放在堆中,公有的
直接内存,它的上限是基于服务内存
堆内存与直接内存的区别:堆内存需要申请,直接内存速度更快
方法区和堆是公有的;Java栈,本地方法栈和PC寄存器都是私有的
程序计数器
- 当前线程所执行的字节码的行号指示器,指向虚拟机字节码指令的位置
- 被分配了一块较小的内存空间
- 针对于非Native方法:是当前线程执行的字节码的行号指示器;针对于Native方法:则为undefined
- 每个线程都有自己独立的程序计数器,所以,该内存是线程私有的
- 这块区域是唯一一个在虚拟机中没有规定任何OutOfMemeryError情况的区域
本地方法栈
native,栈都是跟方法相关的
虚拟机栈,栈
非native方法,也是最常用的方法
包含入参,出参,中间计算,方法异常……
栈帧,私有
垃圾回收器
回收垃圾
执行引擎
执行虚拟机加载进来的字节码
类加载![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d06988f3dcaa70f17e765bf061388176.png)
加载
流。inputstream Resource
将class文件读取进来,读到的就是二进制的数据流
并将二进制流放入方法区中(永久代,元空间)
验证
验证加载进来的二进制数据流是不是合法的,合规的
准备
为类变量分配内存
为类变量分配初始值
解析
什么是符号引用,什么是直接引用
将符号引用转化成直接引用
初始化
类构造器< init >方法
符号引用和直接引用
Java -verbose
为什么会有符号引用?
因为在编译完java文件后,会生成一个class文件,此时JVM并不知道引用的内存地址,所以采用符号引用来表示类中内容的引用关系
虚拟机栈&本地方法栈
- 虚拟机栈为执行Java方法服务,是描述方法执行的内存模型
- 栈是线程私有的内存空间
- 每次函数调用的数据都是通过栈传递的
- 在栈中保存的内容为栈帧。它的数据结构就是先进后出。每当函数被调用,该函数就会被入栈,每当函数执行完毕,就会执行出栈操作。而当前栈顶,即为正在执行的函数
- 每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、帧数据区、动态链接、方法出口等信息
- 本地方法栈帧是为本地方法服务等
栈帧
同一方法,提升最大栈空间
增加局部变量表对栈空间的占用
堆
- 运行时数据区,几乎所有的对象都保存在java堆中
- java堆事完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理,而不需要显式地释放
- 堆是垃圾收集器进行GC的最重要的内存区域
- Java堆可以分为:新生代(Eden区,S0区,S1区)和老年代
- 在绝大多数情况下,对象首先分配在Eden区,在一次新生代GC回收后,如果对象还存活,则会进入S0或S1,之后,每经历过一次新生代回收,对象如果存活,它的年龄就会加1。当对象的年龄达到一定条件后,就会被认为是老年代对象,从而进入老年代
方法区
- 逻辑上的东西,是JVM的规范,所有虚拟机必须遵守的
- 是JVM所有线程共享的、用于存储类信息,例如:类的字段、方法数据、常量池等
- 方法区等大小决定了系统可以保存多少个类
- JDK8之前——永久代
- JDK8及以后——元空间
永久代
- 指内存的永久保存区域,主要存放Class喝Meta(元数据)的信息,Class在被加载的时候被放入永久区域,它和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常
- 如果系统使用了一些动态代理,那么有可能会在运行时生成大量的类,从而造成内存溢出。所以,设置合适的永久代大小,对于系统的稳定性是至关重要的
- -XX:PermSize
设置初始永久代大小。例如:-XX:PermSize=5MB
- -XX:MaxPermSize
设置最大永久代大小,默认情况下为64MB。例如: -XX:MaxPermSize=5MB
元空间
- 在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代
- 元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用堆外的直接内存
- 因此,与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存
- -XX:MaxMetaspaceSize
设置最大元数据空间。例如:-XX:MaxMetaspaceSize=20MB
为什么使用元空间替换永久代?
1.为永久代设置空间大小是很难确定的
2.对永久代进行调优是很困难的
JVM垃圾回收算法
什么是垃圾回收
GC:垃圾回收,Garbage Collection
垃圾:特指存在于内存中的、不会再被使用的对象
回收:清除内存中的“垃圾”对象
C++中,对象是由程序员创建的,也是由程序员销毁的,也就是说”对象的一生“都由程序员掌控,所以程序员既是权力最高的皇帝,也从事着最低层的工作
在Java中,程序员只负责创建对象,对象的销毁是由JVM控制的
可触及性
- 什么是可触及性
就是GC时,是根据它来确定对象是否可被回收
也就是说,从根节点开始是否可以访问到某个对象,也说明这个对象是否被使用
- 可触及性分为3种状态
可触及:从根节点开始,可以到达某个对象
可复活:对象引用被释放,但是可能在finalize()函数中被初始化复活
不可触及:由于finalize()只会执行一次,所以,错过这一次复活机会的对象,则为不可触及状态
四种引用级别
- 强引用
就是一般程序中的引用,例如Student student = new Student();
- 软引用
当堆空间不足时,才会被回收。因此,软引用对象不会引起内存溢出
- 弱引用
当GC的时候,只要发现存在弱引用,无论系统堆空间是否不足,均会将其回收
- 虚引用
如果对象持有虚引用,其实与没有引用是一样的。虚引用必须和引用队列在一起使用,它的作用是用于追踪GC回收过程,所以可以将一些资源释放操作放置在虚引用中执行和记录