目录
JVM三大区
1.堆
2.方法区(元数据区)
3.栈空间
JVM类加载机制
1.加载
2.验证,
3.准备 给类对象分配内存
4.解析
5.初始化
双亲委派模型
一个类什么时机会被加载 懒汉模式用了才加载
类卸载:将类干掉
垃圾回收(GC)
垃圾回收分两步
方案1引用计数方案,存在两个缺陷
方案2.可达性分析,java中实际采用的方案,
如何清理垃圾,释放对象
jvm中最大也最重要的一个空间,放你新new()出来的对象
放类对象。。calss文件被加载到内存后放的地区。类对象,方法内容,static成员
剩余空间放的是线程,每个线程里面都是栈,和一个程序计数器。
栈放的是方法的一个调用的关系。
栈空间和程序计数器是每个程序有一份
public class bit1019{
public int n =10;
public static int a=10;
public static void main(String[] args) {
bit1019 b = new bit1019();
}
}
n是普通成员变量,就包含在new的对象内部,堆上。
a是一个静态成员变量,是包含在类对象中,方发区的
t是一个局部变量,处于栈上
把类从硬盘文件加载到内存中去
java程序,最开始写一个,java文件。编译成。class文件字节码
运行java程序,jvm就会读取。class文件,把文件的内容,放到内存中去。并且构成。class对象。
找到。class文件,打开文件,读取文件内容,并尝试解析格式
检查看一下当前。class文件的格式,是否符合要求
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
。clsss二进制文件内部的结构
u4相当于unsigned int u2就是unsign short
magic魔幻数字,一个二进制文件开头,通常都有一个magic区分文件的二进制格式
minor_version和maior_version表示主版本号和小版本号
constant_pool_count;表示常量池的最大长度
cp_info constant_pool[constant_pool_count-1];常量池数组
u2 access_flags;当前类是否是一个public 类
u2 this_class;这个类的一些相关信息
u2 super_class;父类的相关信息
u2 interfaces_count;这个类实现的一些接口数量
u2 interfaces[interfaces_count];实现的接口
u2 fields_count;文件属性
field_info fields[fields_count];文件数据
u2 methods_count;文件方法的计数
method_info methods[methods_count];文件方法
u2 attributes_count;注解
attribute_info attributes[attributes_count];注解
这个和java文件中对应
最终的目标,是构造出完整的类对象,分配内粗+初始化
分配出来的内存空间,内容就是全0的值,此时此刻,类对象上的static成员也都是0
主要是用来初始化类对象中涉及到的一些字符串常量。
其实字符串常量已经在class文件中有了,直接读到内存中就行了。就是将常量池内的符号引用替换为直接引用
对类对象进行更具体的初始化操作,初始化静态成员,执行静态代码块,加载父类
static {
}
JVM加载class文件时会用到一个类加载器模块。JVM自带了三个
1.Bootstrap ClsassLoader ,负责加载标准库中的类,java有一个标准文档,描述了都要加载那些类
2.Extension ClsassLoader 负责加载jvm扩展的库,除了标准库之外,实jvm的厂商可能还会再添加一些类
3.Appilication ClsassLoader负责加载第三方库, 像mysql jdbc等等
存在父子关系,上父下子
双亲委派模型,描述了类加载的流程。
1.从Appilication ClsassLoader开始,不会立即搜索第三方库,先把任务委派给父亲,让父亲先加载。
2.到了Extension ClsassLoader,也不会立即就搜索拓展库的目录,也是先把加载任务委派给父亲,让父亲先加载。
3,到了Bootstrap ClsassLoader ,也不会立即就搜索拓展库的目录,也是先把加载任务委派给父亲,让父亲先加载。但没有父亲,就自己动手搜索,招到了就执行加载,没找到就把任务给儿子,
4.如果一路到了Appilication ClsassLoader,还没有找到就会抛出一个异常
1.构造类的实例
2.使用了类的静态方法,静态属性
3.子类的加载会触发父类
类加载了之后,后续使用就不必加载了
一般来说,类加载了之后,就不许考虑卸载了,一致保持到程序运行结束,有的特定场景,可能需要用到热补丁,不重启服务器照样更新,通过特殊手段,把需要的类卸载掉,直接使用加载好的类替换
c语言中申请内存有两种方式。
1.定义局部变量,全局变量
2.使用malloc申请内存。此时申请的内存,如果不手动释放,内存就会持续存在,一直到程序结束。服务器,追求的是7*24小时运行,不能随便重启,如果申请内存不释放,就会使剩下的内存,阅历啊越少,最红内存没饿了,后续申请就失败=>进程出现严重的bug,甚至崩溃
c语言中,就需要程序员,通过手动调用free函数,来释放内存。非常依赖人。
java中引入了垃圾回收机制,自动的判定某个内存是否会继续使用,如果不会,就会自动把垃圾给释放掉,后续的一些语言,大部分都都引入了这个机制
1.判定对象是否使垃圾
如果一个对象,再后续的代码中,不会被继续使用到了,就可以视为垃圾
2.释放对象的内存
1.空间利用率低,浪费更多的空间,2.可能存在循环引用的问题,导致对象不能被正确识别为垃圾。
JVM首先会从现有的代码中能直接访问到的引用出发尝试遍历所有能访问的对象,只要对象能访问到,就会标记可达,完成整个遍历之后,可达之外的对象,就是不可达,也就相当于是垃圾了
gc roots(从那些对象开始出发,进行这样的扫描)
1.栈上的局部变量
2.常量池里的引用
3.方法去中的静态成员
1.标记清除(直接释放)
直接释放对象,及可能会引起内存碎片。申请内存的时候都是申请的连续的内存空间,释放内存,就可能会破会啊原有的连续性,导致有内存但是申请不了。
2.复制算法,通过冗余的内存空间,把有效的对象复制到另一部分空间,来避免内存碎片,
把一个内存分为两份,用一份丢一份。把左侧区域中有效的对象复制到右侧
缺点,浪费内存。
3.标记整理,顺序表删除元素,搬运
避免了内存利用率低的问题,但搬运的成本还是比较高的、
4分代回收
java代码中,对象也就分成大体两类。
1.生命周期特别长
2.生命周期特别短
按照对象的GC,来制定不同的回收策略。
一个对象,经过一次GC就算长了一岁。
整个堆被分为两个部分新生代,老年代
新生代分为伊甸区和生存区,伊甸区的如果熬过一轮GC还没有挂掉,就会被向后挪一步,如果经过很久还没有挂就会去老年代。