JAVA 中的类
一:类的生命周期
类的生命周期从类被加载,连接和初始化开始!
到类的卸载结束!
01.类的生命周期中,类的2进制数据位于方法区;
02.在堆中会有一个描述这个类的Class对象;
2.1 加载: 需要类加载器
将class字节码文件内容加载到内存中,并将这些静态数据转化成
方法区中运行时数据结构!在堆中生成一个Class对象;
这个Class对象就是方法区类数据的访问入口!
2.2 链接:
01.类的验证 在编译期间
合法???
001.类文件的结构检查
002.语义检查 比如说final修饰的变量在编译期间发现再次赋值
003.字节码验证,确保字节码能被jvm识别
02.类的准备
为类中所有static修饰的内容开辟空间,并且赋予初始值!!!
static int num =100;
这时候 num=0;
03.类的解析
把类中所有的符号引用转换成直接引用!
符号引用:就是用字符串的形式来表示某个变量或者是类,我们能看懂
String a="a"; Student stu=new Student();
a,stu都是符号引用!
直接引用:是根据符号引用翻译出来的地址!
2.3 初始化
加载不是初始化!
初始化指的是实例化!创建出类的实例!
初始化的时机:
01.类的主动引用
001.new一个类的对象
002.通过反射的newInstance()
003.再初始化子类的时候必须先初始化父类
02.类的被动引用
001.通过类名访问静态的内容
002.调用类的静态常量也不会初始化类
003.用类作为对象数组存在时,也不会初始化类
004.子类调用父类的静态变量不会加载子类的静态代码块(不会执行类的准备阶段)
三:垃圾回收机制(GC)
3.1:为什么需要垃圾回收机制
01.只要是对象被创建,那么就会在虚拟机的堆中开辟空间;
02.程序运行过程中会创建N个对象,每个对象都会有自己的空间;
03.如果每个对象都永久的占有这块内存空间,显然内存是不够的!
为了保证其他的对象能够被正确的创建!
在C语言中,垃圾回收的任务是程序猿自身负责!
可能出现的问题:
01.由于程序猿的粗心大意,导致没有及时释放不使用的对象,释放错误!
02.程序猿一旦释放了程序核心对象(比如说系统对象),系统崩溃!
3.2:垃圾回收机制的定义
在java程序运行过程中,JVM中有一个专门负责回收那些不再使用的对象所占用的内存!
这种回收的过程,我们称之为垃圾回收机制(GC,Garbage Collection)!
3.3:垃圾回收机制的特点
01.减轻了程序猿进行内存管理的负担;
02.防止系统内存被非法释放,使我们的程序更加健壮;
03.只有在对象不被任何变量引用时,才能被回收;
04.程序无法强制让垃圾回收器立即回收垃圾操作;
05.当垃圾回收器将要回收不用对象的内存时,会先调用这个对象的finalize()
这个方法有可能使对象复活,导致垃圾回收器取消对该对象内存的回收。
3.4:对象的状态
在java虚拟机的垃圾回收器来看,堆中所有的对象都有3种状态!
01.可触及状态
02.可复活状态
03.不可触及状态
只有对象处于不可触及状态的时候,垃圾回收期才会真正的释放对象所占有的内存!
1.对象的生命周期开始 (new语句或者是反射中newInstance) 可触及状态
2.对象不在被引用,或者对象调用了finalize() 可复活状态
3.对象调用了finalize() 不可触及状态
4.垃圾回收
5.对象的生命周期结束
3.5:垃圾何时被回收(触发GC的条件)
01.对象没有引用===》对象处于不可触及状态
02.程序在作用域正常执行完毕之后
03.System.exit()
04.程序以外终止
3.6:详解GC
01.什么时间===》触发GC的条件
001.System.gc()可能触发;
002.系统自身决定GC的触发时机
根据Eden区和From Space区的内存大小来决定!
02.对什么东西
java中的对象===》通过分析算法无法找到的对象
03.做了什么
对搜索到的对象进行复制操作;
对搜索不到的对象执行finalize()。
如果真正的回收一个对象,要至少需要两次标记!
01.第一次标记:对于一个没有其他对象引用的时候;执行finalize()
02.第二次标记:针对于筛选过的对象,进行回收。
3.7:java中内存分配机制
核心:分代分配,分代回收!
新生代(Young Generation)
Eden 区 :是连续的内存空间,所以分配内存极快
Survivor 区 :From To 必须有一个区域是空的=
老年代(Old Generation)
永久代(Permanet Generation)===》方法区
回收两种数据:
01.常量池中的常量
02.无用的类信息
什么时候回收?
01.类所有的实例都被回收了
02.加载类的ClassLoader也被回收了
03.类对应的Class对象没有被引用
注意点:
01.绝大多数创建的对象被分配在Eden区,但是大多数对象会在这个区域死亡!
02.当Eden区满的时候,会执行Minor GC(Young GC),将死亡的对象清理掉,
剩余存活的对象存放到survivor0区(survivor1区必然为空)
03.以后每次Eden区满的时候,会执行Minor GC,所有存活的对象又被放进survivor0区
04.当survivor0区满的时候,会把存活的对象复制到survivor1区,之后清空survivor0区!
05.反复执行之后,仍然活着的对象复制到老年代!
06.老年代的内存比年轻代要大,但是也有满的时候,当老年代满的时候执行Major GC(Full GC)
07.如果对象比较大,年轻代存放不下,就会直接把对象放进老年代!
Young GC和Full GC的区别:
01.Young GC是在新生代的Eden区满,Survivor区满的时候触发
Full GC 是在老年代满的时候触发
02.Young GC执行的频率高
Full GC 执行的频率低
什么时候触发Full GC
01.执行System.gc(),系统建议执行Full GC,但不是必须执行
02.老年代空间不足
001.当Young GC执行后存活的对象需要进驻老年代,但是发现老年代空间不足
002.当Survivor0区,Survivor1区来回切换时,发现内存不足,必须放进老年代,而且老年代空间不足
03.方法区空间不足
3.8:GC涉及到的算法
01.引用计数法 Reference Counting
给对象增加一个引用计数器,使用一次引用+1,少一个引用-1!
当引用为0的时候回收!
但是不能解决循环引用的问题!
02.根搜索算法 GC Roots Tracing
对象以根作为起点,向下延伸,延伸的路径我们称之为 引用链(Reference Chain)
当一个对象没有任何引用链连接时,证明这个对象时不可用的对象(不可达),会被回收!
什么是根?
01.方法区中常量引用的对象
02.栈中引用的对象
03.静态属性引用的对象
03.复制算法 Copying
就是将原有的内存空间分为两块,每次只能使用其中一块!
缺点就是浪费一半的内存空间!比标记-清除算法要高效!
不适合存活对象较多的区域使用!
在新生代使用!
04.标记-清除算法 Mark-Sweep
001.把所有存活的对象打个标记
002.把所有没有标记的对象统一清除
缺点:
01.两个过程效率都慢,因为需要想查询
02.因为在清除过程中会产生内存碎片,如果有大对象将无法存储
04.标记-整理算法 Mark-Compact
适合存活对象多的区域,所以适合在老年代使用!