<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path> </svg>
讯享网

讯享网

线程私有:程序计数器,虚拟机栈,本地方法栈
线程共享的:堆,方法区,直接内存
1.1 各个区域详解
程序计数器
每个线程需要一个计数器记录自己执行到哪一行了。线程之间切换需要保存自己的执行位置,因此必须每个线程有自己的计数器。
生命周期随着线程创建而创建,随着线程结束而死亡。
java虚拟机栈
由一个个栈帧组成,每个栈帧都拥有局部变量表,操作数栈,动态链接,方法出口等信息。
局部变量表存放了编译期可知的各种数据类型,对象引用。
可能出现两种错误,,
前者如果不允许虚拟机栈内存动态扩展,就可能因为递归导致栈深度超过当前java虚拟机最大深度的时候抛出异常。
后者是虚拟机栈内存大小可以动态扩展,如果虚拟机栈扩展着扩展着,没空间了,就会报错。
本地方法栈
很类似,但是执行的是本地方法。
堆
存放对象实例,之前是所有对象都存放在堆中,现在有些新技术的产生,栈上分配,导致不是所有对象都在堆中了。
如果某些方法中的对象引用没有被返回或者没有被外面使用,没有逃逸出去,那就直接在栈上分配内存。
垃圾回收主要在堆区域。可细分为老年代和新生代。Eden,FromSurvivor,ToSurvivor。

对象一般在Eden中分配,经历一次GC之后,eden和S0中存活的,放到是s1种,下次就是,eden和s1中存活的,放到s0中,并且每次的GC存活,年龄+1。超过阈值就放入老年代中。
方法区
(现在已经不在永久代,而是在元空间)存储已经被虚拟机加载的类信息,常量,静态变量等等。
常用参数:
讯享网
2.1 对象的创建

- 类加载检查:遇到一条new指令,首先检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析、初始化过。如果没有就要先执行相应的类加载过程。
加载过程如下图,下一章节详细说。

- 分配内存:类加载通过以后,虚拟机为新生对象分配内存,对象所需要的内存大小在类加载完之后就确定了。
- 初始化零值:将分配的内存空间,都初始化为0值
- 设置对象头:对象头存放着对象的hashcode,元数据信息,GC分代年龄之类的。
- 执行init方法:此时从jvm的角度看,一个新的对象已经产生了,但是从java的角度看,所有字段都还是0.所以接着执行init方法,把对象按照程序员的意愿初始化,这样真正的对象才算产生出来。
2.2 类加载过程

加载
- 通过全类名获取类的二进制字节流。
- 将字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 内存中生成一个代表该类的Class对象,作为方法区这些数据的访问入口
验证

准备
正式为类变量分配内存并设置类变量初始值。这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在 Java 堆中。而类变量在方法区中。
初始值通常为默认的0,0L,null,false等。public static int value = 11;实际上此时为0,final的话为11.
解析
虚拟机将常量池中的符号引用替换为直接引用的过程。解析动作主要针对类或者接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符七类符号引用进行。
符号引用;一组符号描述目标,可以是任何字面量。直接引用就是直接指向目标的指针、相对偏移量、句柄等等。
举例:jvm给每个类准备了一个方法表,存放类的所有方法。不同的方法肯定在表中偏移量不同,然后调用一个方法的时候,只要知道这个方法在表中的偏移量就可以直接调用。所以解析过程就是把原本的字面量的符号引用,替换为了直接引用。得到了类、字段、方法在内存中的指针或者偏移量。
初始化
类加载的最后一步,执行初始化方法的过程。
只有主动使用类才初始化类
卸载

讯享网
2.3 对象的内存布局
- 用于存储对象自身的运行时数据(哈希码,GC分代年龄,锁状态标志等等)
- 类型指针,对象指向类的元数据的指针,通过这个指针确定这个对象是哪个类的实例。
实例数据:
程序中定义的各种类型的字段内容。
对其填充:
不必然存在,只是为了凑齐8字节的整数倍

MinorGC一直重复这个过程:eden+from区->to区。
to区填满之后,所有对象移步老年代。

3.1 GC分类
部分收集
新生代收集:MinorGC/YoungGC
老年代收集:MajorGC/OldGC
混合收集:MinorGC+部分老年带回收
整堆收集
FullGC
3.2 怎么判断对象已经死亡?

引用计数法
有地方引用它,就+1,引用失效就减1.但是很难解决循环引用的问题。一般不使用这种。
可达性分析

GCRoot对象
- 比如虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区静态属性、静态常量引用的对象
- 所有被同步锁持有的对象
3.3 垃圾收集算法
- 标记清除
- 复制算法
- 标记整理
- 分代收集
标记清除 产生大量碎片

标记复制 空间只能使用一半

标记整理 需要大量移动

分代收集
新生代:主要采用复制算法,但是不是分为两块。而是eden+from->to
老年代:标记清除/标记整理。
3.4 类文件结构

- 访问标志:是不是public的?是不是abstract的?是不是final的?是不是enum的?是不是接口?
- 索引信息:当前类、父类、接口数量、接口信息
- 字段表集合:描述类中声明的变量,包括静态变量和实例变量,不包括方法的局部变量。
- 方法表集合:描述类声明的方法。
- 属性表集合:上述表运用到的信息。
3.5 类加载方式
- BootstrapClassLoader:加载javahome/lib目录下的jar包
- ExtentionClassLoader: 加载jrehome/lib/ext 下的jar包,或者被java.ext.dirs系统变量指定的路径下的jar包
- AppClassloader:面向用户的加载器,加载当前应用classpath下的jar包和类
双亲委派模型

好处:保证了java程序的稳定运行。避免类的重复加载,也保证了java的核心API不被篡改。





版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/181960.html