Java堆栈
为了鞭策自己,逼迫自己记录日志。以后会每天将自己所学习到内容发到博客,以贴此为证,好好学习,天天向上。
Java 内存运行时数据区
- 程序计数器(线程私有,是当前线程所执行的字节码行号指示器。字节码解释器工作时就是通过改变这个计数器的值来获取下一条需要执行的字节码指令:如分支、循环、跳转、异常处理等)。
- 栈(线程私有,生命周期与线程相同。它描述的是Java执行方法时的内存模型。每个方法在执行时都会创建一个栈帧,它用于存储局部变量表、操作栈);局部变量表存储的是方法入参及方法内部的局部变量,局部变量包括:基本数据类型(short、byte、int、boolean、char、long、float、double)、对象引用、returnAddress。其中long、double占用两个slot空间,其它只占用一个;它是以索引的方法访问;数组的方式存储,它的空间在JVM 编译期间就能完成分配。 操作栈是栈的执行引擎,它是以压栈-出栈的方式访问。引起StackOverflowError ,线程请求的栈的深度超过了Jvm虚拟机所允许的深度。
- 堆(所有线程共享)在虚拟机启动时创建。存放对象的实例,包括所有对象的实例以及数组。每个线程都有一个本地内存(用于存储共享对象的副本),线程结束后会将本地内存刷到主内存中,所以才会线程不安全,需要同步操作。
- 方法区(永久代)也属于堆的一部分,也需要GC回收。它主要存放Class类的信息(元数据如:方法代码)、常量、静态变量、运行时常量池。(JDK1.7后将常量池移入堆中) ,常量池:基本数据类型(Boolean、Character、Long、Short、Byte、Integer)、String都实现了常量池技术,但整型只对-127-127这个范围内的使用常量池,超过这个范围将不再使用。这就是为什么 Integer a = 128; Integer b = 128; a == b (flase)的原因;常量池技术(为了方便快捷的取出一个对象,从池中取出如果不存在就创建一个并放入池中)。String.intern() 如果字符串常量池中已经存在该字符串,那么返回;否则创建一个并添加到常量池中。
- 直接内存(并非虚拟机运行时数据区的一部分);I/O使用中就用的直接内存,它可以使用Native直接分配堆外内存。它不受堆内存大小控制,但是受本机总内存限制,如超过也会报OutOfMemoryError.
对象的创建
- new、克隆、反序列化;
- 分配内存两种方式: 1、如果内存是规整连接的,那中间需要一个指针,指针下移一个与该对象空间大小一致的内存,此方式为“指针碰撞” ; 2、如果内存不是规整的,那虚拟机需要维护一个列表,记录哪些内存可用,并为对象分配内存空间,此方法为“空闲列表”;不管是哪种方式,它是通过垃圾收集器来决定的,像Serial、ParNew是指针碰撞的方式分配内存,而CMS收集器通过 空闲列表的方式分配内存;
- 对象在内存的布局: 对象头、实例数据、对齐填充; 对象头包括:哈希码、GC分代年龄、锁状态等;
对象的访问
- 通过句柄池;Java堆会专门划分一块区域来作为句柄池;对象引用->句柄地址,句柄存放:具体的对象实例指针(指针指像实例数据)、类型数据指针(指针指向对象类型数据->类相关信息吗?) 优势: 在对象被移动时(GC),只需移动句柄池
- 通过指针:直接指向对象地址;优势:更快