内存区域下半场,紧接上部分,线程共享的内存部分。
java堆(java Heap)
主要用于存放对象,为几乎所有线程所共享
根据规范:java堆并不一定需要内存上连续,只需要逻辑上连续即可
由于使用永久代来实现方法区的话,某些情况会出现内存溢出的尴尬问题
于是1.8后更新了 ,使用 Meta space 代替永久代,解决永久代溢出的问题
具体阐述:可以参照文章 duanxz的博客中关于为什么替代掉永久代的问题
方法区 (Method Area alias-> Non-Heap)
主要存储类信息、常量、静态变量等数据
新对象内存的分配
称之为一条new操作的旅程
一、类加载检查过程
首先要去检查参数能否在常量池中定位到引用,并且检测这个对象是否已被加载、解析和初始化过。
二、为该对象分配内存空间
分配内存也存在两种方法,具体系统采用的那种算法需要执行
java -XX:+PrintCommandLineFlags -version
通常附带 compact 算法的收集器 采用指针碰撞,否则使用 空闲列表
①指针碰撞
前提是java堆中内存是绝对规整的情况下(使用过与空闲的内存分明鲜别)
此时,分配空间就像切蛋糕一样轻易便可
②空闲列表
内存是零散的,虚拟机维护了一个对应空闲或使用的内存地址记录表。按需将记录内的空闲内存分配出去
三、分配内存时的并发问题
(虽然java不提指针,但是对于内存地址来说,我还是引用下)
即存在A分配0x11地址,还未把地址指向它,同时B也被指向0x11地址的问题。
①默认处理
虚拟机采用CAS 配上失败重试来保证操作的原子性
②本地线程分配缓冲(Thread Local Allocation Buffer alisa-> TLAB)
将分配内存的操作按照线程划分在不同空间内。哪个线程需要分配内存,则在在哪个线程的TLAB上按需分配,当且仅当TLAB用完同时需要分配新的TLAB时,才需要同步锁定操作(操作内属于线程私有,不需要锁,效率会提高)。
启用方法: ```-XX:+/- UseTLAB```
四、对象初始化
将分配下去的初始内存空间初始化为零值(不包含头对象)
如果采用TLAB,则可以在分配时进行初始化
对象头不被初始化是因为,要将元数据信息,GC分代等信息存储至对象头中
执行开发者意愿的初始化操作