JVM:
- 程序计数器:线程私有,切换字节码来进行不同命令的执行,每个线程独立执行
- 虚拟机栈:存放编译期可知的基本数据类型和对象引用(对象指针),线程私有
- 本地方法栈:虚拟机使用到的native方法的服务,和虚拟机栈类型.由虚拟机厂商自己实现
- 堆:线程共享,管理对象,GC的场所(新生代,老生代)
- 方法区:存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码(类方法),线程共享
- 运行时常量池:方法区的一部分,对于常量池没有具体限制的规范,String则是一个很好的例子
- 直接内存:不属于JVM,即机器的内存
对象内存分配:
- 对象头:对象运行时的数据,hashcode,GC分代年龄,锁状态标识,线程持有的锁,偏向线程ID,偏向时间戳,类型指针(指向元数据的指针,并不是所有对象都有类型指针,如果是数组对象,还应该有存储数据长度的一块数据)
- 实例数据:对象真正存储的有效数据,各种字段内容
- 对齐填充:不是必然存储,hotspot的自动内存管理要求对象大小必须是8字节的整数倍,而对象头正好是8字节的整数倍,所有实例数据部分如果没有对齐时,就需要通过对齐来自动填充
GC算法:
- 引用计数算法:给对象中添加一个引用计数器,当一个地方引用它时,计时器就加1,当引用失效时,计数器就减1,任何对象的计数器为0时就是不可能再被使用了,但是如果出现了互相引用,但是该对象已经没有被其他任何对象引用,就会导致计数器永远不为0,导致无法gc
- 可达性分析算法:通过GC Root作为起点,当一个对象到GC Root没有任何引用链相关时,则证明该对象不可用,将被判断对象不可用
GCRoot的对象包括:
- jvm中引用的对象(int,long,boolean,float,double,byte,char,reference的数据)
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象(final 的常量值)
- 本地方法栈JNI的应用对象(即native方法)
JAVA应用:
- 强引用:Object obj = new Object()这类引用,只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象
- 软引用:用来描述一些还有用但并非必须的对象,对于软引用关联着的对象,在系统将要发生内存溢出异常之前,会将这些对象列入回收范围,并进行二次回收,如果这次回收还是没有足够的内存,才会出现内存溢出的异常,通过SoftReference实现
- 弱引用:用来描述非必须对象,强度比软引用要更弱,被弱引用关联的对象只能存活到下一次垃圾回收之前,当垃圾回收器开始工作时,无论当前内存是否足够,都会被回收,通过WeakReference实现
- 虚应用:最弱的一种引用关系,一个对象是否设置虚应用,完全不会影响其生存时间,也无法通过虚引用来取得一个对象的实例,设置虚引用唯一的目的就是能在这个对象被回收的时候拿到一个系统通知,通过PhantomReference实现
类加载时机:
加载->验证->准备->解析->初始化->使用->卸载
有且只有5种情况会必须进行初始化
- 遇到new,getstatic,putstatic,或者invokestatic这4个字节指令时,如果类没有进行初始化,则需要先触发其初始化,生成这4条指令的最常见的java代码场景就是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰,已经在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候
- 使用反射包中的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
- 当初始化一个类的时候,如果发现其父类还没有进行初始化的时候,则需要先触发其父类的初始化(如果是接口,并不要求父接口完全都完成了实例化)
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
- 当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,并且这个句柄的类没有进行初始化,则需要先进行初始化