1:object的finalize方法
java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize()。
(1).对象不一定会被回收。
(2).垃圾回收不是析构函数。
(3).垃圾回收只与内存有关。
(4).垃圾回收和finalize()都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的。
Java 1.1 通过提供一个System.runFinalizersOnExit() 方法部分地解决了这个问题。(不要将这个方法与 Java 1.0 中的System.runFinalizations() 方法相混淆。)不象System.gc() 方法那样,System.runFinalizersOnExit() 方法并不立即试图启动垃圾回收器。而是当应用程序或 Applet 退出时,它调用每个对象的finalize() 方法。
2:强弱软虚四种引用。
Reference抽象类的子类作用:
- 可以让程序员通过代码的方式来决定某个对象的生命周期;
- 有利于垃圾回收。
- 强引用;
Object o = new Object();
断开引用:
o=null;
- 软引用:
软引用只会在内存不足的时候才触发
软引用就是把对象用SoftReference包裹一下
软引用特点:当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。在高速本地缓存Caffeine中实现了软引用的缓存,当需要缓存淘汰的时候,如果是只有软引用指向那么久会被回收。
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());
Student student = studentSoftReference.get();
- 弱引用:弱引用的特点是不管内存是否足够,只要发生GC,都会被回收:
WeakReference<Student>studentSoftReference=new WeakReference<Student>(new Student());
Student student = studentSoftReference.get();
- 虚引用
1:无法通过虚引用来获取对一个对象的真实引用。
2:虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);
System.out.println(reference.get());
虚引用使用场景:
一般可以通过虚引用达到回收一些非java内的一些资源比如堆外内存的行为。例如:在 DirectByteBuffer 中,会创建一个 PhantomReference 的子类Cleaner的虚引用实例用来引用该 DirectByteBuffer 实例,Cleaner 创建时会添加一个 Runnable 实例,当被引用的 DirectByteBuffer 对象不可达被垃圾回收时,将会执行 Cleaner 实例内部的 Runnable 实例的 run 方法,用来回收堆外资源。
3:JAVA的堆外内存(基于NIO的ByteBuffer类实现)
G1 GC=新+幸存+老+巨+region=(所有的region)
堆外内存
1.使用未公开的Unsafe。
2.NIO包下ByteBuffer。:使用方式:ByteBuffer.allocateDirect(10 * 1024 * 1024)。-XX:+DisableExplicitGC使用后堆外内存则无法释放。是靠system.gc来释放的。
4:JDK8元空间metaspace代替持久代(Permanent Generation space)
JDK8之前:堆内内存 = 新生代+老年代+持久代(存放Class和Meta的信息)
JDK8后:持久代被不在存在,(metadata被放在metaspace)
metaspace和持久代
相似:JVM规范中方法区的实现
不同:持久代在JVM中,metaspace在JVM外,本地内存
metaspace的组成
Klass Metaspace:-XX:CompressedClassSpaceSize参数来控制
1:Klass Metaspace就是用来存klass的,klass是我们熟知的class文件在jvm里的运行时数据结构,
2:Klass Metaspace在关闭压缩指针开关就没有。在堆内存大于32G时,会关闭压缩指针开关。
3:最多只会存在一块。
NoKlass Metaspace:
1:专门来存klass相关的其他的内容,比如method,constantPool等,
2:多块内存组合起来的,必须要有。
3:叫NoKlass Metaspace但可以存klass
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。 对于僵死的类及类加载器的垃圾回收将在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。
除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
5:JVM
java运行一个虚拟机运行一个程序,多个程序运行多个,多个虚拟机间不可共享
JVM:1 官方的HotSpot 2 BEA公司的JRockit 3 IBM公司的J9 JVM 4:Taobao JVM阿里的JVM等,每个版本的JVM并不完全相同。主要是Orcle的HotSpot JVM
JVM内存区包括:
线程共享:方法区,堆。
线程私有:虚拟机栈,程序计数器,本地方法栈。
- 程序计数器(PC Register):每个线程都有一个程序计算器,是指由执行引擎读取下一条指令的一个指针,可以看做是当前线程所执行的字节码的行号指示器。是一个非常小的内存空间
- 本地方法栈(Native Method Stack):用于登记native方法,在Execution Engine执行时加载native libraies。运行比如C语言的程序。
- 虚拟机栈(VM Stack):java 方法执行的内存模型,线程私有的,线程创建,栈创建。线程结束,栈释放。
每个方法被执行的时候 都会创建一个“栈帧”用于存储局部变量表(包括参数)、操作栈、方法出口等信息。 (局部变量表:存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(引用指针,并非对象本身)
每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
每当启用一个线程时,JVM就为他分配一个JAVA栈,栈是以帧为单位保存当前线程的运行状态
栈是由栈帧组成,每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧
栈帧帧是由局部变量区、操作数栈和帧数据区组成
类型信息确定此方法局部变量区和操作数栈的大小
局部变量区:long和double是两个字长,其它类型都是一个字长(4*8位),引用通过指针访问,也是一个字长
操作数栈:可把操作数栈理解为存储计算时,临时数据的存储区域。通过入栈和出栈来访问的。不是通过索引来访问
帧数据区:java栈帧通过帧数据区存的数据来支持常量池解析、正常方法返回以及异常派发机制
- 方法区( Method Area):方法区和持久代不可以划等号(持久代是HotSpot JVM才有的)
用于存储虚拟机加载的:静态变量+常量+类信息+运行时常量池
(类信息:类的版本、字段、方法、接口、构造函数等描述信息 )
(运行时常量池中保存着一些 class 文件中描述的符号引用,同时还会将这些符号引用所翻译出来的直接引用存储在运行时常量池中)
默认最小值为16MB,最大值为64MB,可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小
- 堆(Java Heap):所有的对象实例以及数组都要在堆上分配,此内存区域的唯一目的就是存放对象实例
堆是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.
每种收集器,对应的并不完全相同。
栈空间不足:java.lang.StackOverFlowError。
堆空间不足:java.lang.OutOfMemoryError。
- 直接内存(Direct Memory)
直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存
JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由C语言实现的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引用
由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。
上图