深入理解jvm的第二版(基于jdk1.7),一定要看下第三版
注意规范和实现的差距,规范没有的,实现可以有,规范定义的,实现的方式也未必是死板的一对一
jvm规范定义了6个运行时数据区:pc,jvm栈,本地方法栈,堆,方法区,运行时常量池
jvm栈(局部变量表编译期大小确定,stackoverflow:栈深度太大超-Xss,oom:申请不到内存,比如线程太多了)
native方法栈(sof和oom)
pc(当前字节码指令行号,native方法为空,无oom)
堆(对象,数组,新生代(Eden,from/to survivior)老年代,-Xms初始 -Xmx最大,经常这两个配一样大避免堆动态扩展,内存抖动)
方法区(静态变量,类信息,常量,oom,hotspot以前即1.6用永久代实现 -XX:PermSize -XX:MaxPermSize)
运行时常量池(属于方法区,Class文件(静态)常量池:字面量(常量的值和字符串,比如变量名就可以是一个字符串)和符号引用 运行时常量池:字面量和符号引用和直接引用 动态性:运行时添加常量如String.intern() ,oom)
字符串常量池(属于运行时常量池):StringTable(hashtable实现) 存储指向String对象的索引,1.7普通String和常量池字符串并无差别,仅仅是多了StringTable的指向
常量:值不变(final)的变量
jvm规范,逻辑概念:堆,方法区
hotspot,物理概念:堆,永久代/元空间
运行时常量池永远都属于方法区(因为在概念定义里常量在方法区内),但是方法区在实现上永久代-永久代+堆-元空间+堆
移除永久代:1.7把常量池和静态变量放入了堆中(譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap),永久代就很轻了,1.8永久代替换为元空间
code cache(jit):规范未定义
直接内存 (ByteBuffer.allocateDirect() 返回的DirectByteBuffer去操纵,非jvm进程内存,性能不错(避免在java堆和native堆来回复制数据),oom)
对象:内存分配:指针碰撞(垃圾收集器带compact功能,保证堆内存规整) 空闲列表(在空闲内存列表中找一块分配)
多线程同时分配内存,为保证线程安全,两种方案:1 cas加失败重试 2 每个线程在自己的TLAB里分配
内存初始化为零值,调用init方法(invokespecial)
对象头 Mark Word(32/64bit 记录hashcode,gc分代年龄,锁标志…),类型指针(指向方法区类型数据,不一定有,hotspot有),若为数组记录长度
句柄访问:对象移动(GC)不用改变reference
直接指针:更快,少一次指针定位,hotspot使用,对象头放类型指针
实例数据
对齐填充,保证对象大小为8字节倍数,保证对象起始地址为8字节倍数