学习总结
为了增加记忆,把最近学习的写在博客上,总结第二章Java内存区域与内存溢出异常,第三章垃圾收集器与内存分配策略,第十二章Java内存模型与线程和第十三章线程安全与锁优化的知识点。
第二章Java内存区域与内存溢出异常
jvm把自己获得到的内存分为几个部分
黑色的是线程共享的,红色的是线程独享的
程序计数器:
- 作用:当前线程所执行的字节码的行号指示器,用来控制分支跳转和取下一条指令,和os中cpu的程序计数器差不多
- 特点:每个线程都有,互相不影响。线程执行java方法时,记录的是正在执行的虚拟机字节码地址,Native方法时,这个计数器值为空(undefined),在此内存区域没有规定out of memory error
Java虚拟机栈
- 作用:用来描述java方法执行的内存模型,方法在执行时创建栈帧,用来存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 特点:线程私有,生命周期与线程相同,有两种异常:stack over flow error和out of memory error
本地方法栈
- 作用:用来描述Native方法执行的内存模型,在hotspot虚拟机中与虚拟机栈合二为一
Java堆
- 作用:存放对象实例,几乎所有对象都在这里分配内存,也是垃圾回收的主要区域,分为新生代和老年代,还有Eden 、from survive和to survive空间。
- 特点:在虚拟机启动时创建,所有线程共享
方法区
- 作用:存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码
HotSpot虚拟机
-
对象的创建
①当虚拟机遇到一个new指令时,先去常量池中找类的符号引用,并检查是否已经被加载、解析和初始化,如果没有就先进行类的加载
②加载后虚拟机为新生对象分配内存
③内存分配后,虚拟机将分配到的内存空间初始化为零值
④虚拟机对对象进行设置,比如对象是哪个类的实例,如何找类的元数据信息,对象的哈希码、gc分代年龄等,放到对象头中。
之后进行方法之类的java方法 -
对象的内存布局
对象在内存中存储的布局分为三块区域:对象头,实例数据和对齐填充。对象头:包含两部分①Mark Word,用来存储对象自身运行时的信息,如哈希码,GC分代年龄等。②类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定对象是哪个类的实例。
实例数据:代码中定义的各种类型的字段
填充:占位用,可有可无
-
对象的访问定位
Java程序通过栈上的reference数据来操作堆上的具体对象。主流的访问方式有使用句柄和直接指针两种。
句柄方式:Java堆中划分出一块内存作为句柄池,reference存放对象的句柄地址,句柄中包含了对象实例数据和类型数据各自的具体地址信息。
直接指针方式:堆中需要考虑数据怎么存放类型数据的相关信息,reference中存储的就是对象地址
优缺点对比
句柄:好处是reference中存储的是稳定的句柄地址,对象移动时,只会改变句柄中的实例数据指针,reference本身不需要改变。
直接指针:快,节省了一次指针定位的时间。