java虚拟机内存管理
每个线程就是一个顺序的执行单元,线程共享区即多个线程共享同一块区域,线程独占区即每个线程都有自己的虚拟机栈,本地方法栈,程序计数器。
程序计数器是一个比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器,位于线程独占区,如果线程执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是native方法,计数器的值为underfined
Java虚拟机栈
虚拟机描述的是java方法执行的动态内存模型
栈帧
每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成,用于存储变量表,操作数栈,动态链接,方法出口等。
局部变量表
存储编译期可知的各种基本数据类型,引用类型,returnAddress类型,
局部变量表的内存空间在编译期完成分配,当进入一个方法,这方法需要在帧分配多少内存是固定的,在方法运行期是不会改变局部变量表的大小(存储的只是对象的引用)
1 public class Demo { 2 3 public void tes() { 4 System.out.println("方法执行...."); 5 tes(); 6 } 7 8 public static void main(String[] args) { 9 new Demo().tes(); 10 11 } 12 13 }
方法执行....
方法执行....
........
Exception in thread "main" java.lang.StackOverflowError
若不限定栈的内存,将超过虚拟机的内存,或物理内存,将抛出 OutofMemory
本地方法栈
虚拟机栈为虚拟机执行java方法服务,本地方法栈为虚拟机执行native方法服务
java堆
存储对象实例
垃圾收集器管理的主要区域
新生代,老年代
方法区
存储虚拟机加载的类信息(类的版本,字段,方法,接口),常量,静态变量,即使编译器编译后的代码等数据。
方法区和永久代
垃圾回收在方法区的行为(针对常量池的回收以及对象类型的卸载等)
异常的定义
OurOfMemoryError
运行时常量池,属于方法区的
1 public class Test { 2 3 public static void main(String[] args) { 4 String s1 = "abc";//字节码常量 5 String s2 = "abc";//字节码常量 6 System.out.println(s1 == s2); 7 8 String s3 = new String("abc"); 9 10 System.out.println(s1 == s3); 11 12 System.out.println(s1 == s3.intern());//运行时常量 13 14 } 15 16 /** 17 * true 18 false 19 true 20 */ 21 }
任何字符串的创建都会放在常量池中,常量池在方法区中,运行时常量池维护了StringTable字符串表,数据类型可以是HashSet ,存放所实例的字符串对象,由于hashSet的无序和不可重复, abc 只创建了一个实例,即 s1 == s2
如果用new 创建一个对象一定是在堆内存开辟空间,不再考虑常量池的问题
intern()作用在jdk1.7之后是查看常量池中是否存在和调用方法的字符串内容一样的字符串,如果有的话,就返回该常量池中的字符串,若没有的话,就在常量池中写入一个堆中该字符串对象的一个引用,指向堆中的该对象,并返回该引用。即是调用这个方法之后把字符串对象加入常量池中。
直接内存
能够分配堆外内存,不受到java虚拟机内存的制约,会受到当前操作系统物理内存的制约
对象的结构:
Header 对象头
自身运行时数据(Mark Word)
哈希值 GC分代年龄 锁状态标志 线程持有的索 偏向线程ID 偏向时间戳
类型指针
对象指向它类的元数据的指针
InstanceData(相同的字段分配在一起)
Long dobles shorts/chars
Padding,对齐填充不是必须的,占位符,对象的大小必须是8个字节的整数倍。
对象的访问定位
使用句柄:指向堆中的句柄池,保存了实例对象的地址,引用地址不需要修改。
直接指针,从引用类型直接指向真正的内存区域,速度快,性能高
两者都要保存 到对象实例数据的指针和到对象类型数据的指针