JVM内存区域详细

JVM

运行时数据区域

在JDK1.8之前,运行时数据区域分为(堆、方法区)线程共享,进程(线程私有,包括虚拟机栈,本地方法栈,程序计数器)和直接内存,在JDK1.8之后,方法区直接内存开辟了空间叫元空间,讲方法区替代。

程序计数器–线程私有
内存空间较小,可以看做当前线程执行字节码的行号指示器。字节码解释器通过改变计数器值来让下一条字节码指令,分支,循环,跳转,异常处理等,耶为了每个线程跳转到正确的执行位置,每个线程有一个独立的计数器。它的生命周期随线程创建和终止。 
虚拟机栈–私有,周期同线程一致
JAVA虚拟机栈,通常的栈就是指的虚拟机栈中的局部变量表,由一个一个栈帧组成,包括局部变量表,操作数栈,动态链接,方法出口。
局部变量表主要存放编译器的八个数据类型、对象类型(reference)
常见错误:StackOverFlow 栈溢出,内存大小不允许扩展
         OutMemory    内存溢出,没有多余空间且GC同样没有内存
 每一次函数调用都有栈帧被压入栈中,调用结束会弹出
 return和抛出异常都会被弹出
本地方法栈–与虚拟机栈类似
堆–线程共享
Java中内存最大的一块:所有线程共享区域,存放对象实例,(1.7开始有例外,要是对象引用为返回或者使用,就直接栈上分配),也是GC的主要区域,称之为GC堆
堆内存被分为,新生代,老生代,永生代,JDK8之后,永生带取消变成元空间,直接内存。
对象首先在Eden区域分配,在一次GC后,还存后就会进入s0,s1并且对象年龄加1,一般15岁就会晋升老生代,阈值可以设置。
容易发生内存溢出错误。
方法区–线程共享
用于存储一杯虚拟机加载的类信息,常量,静态变量,及时编译的代码。方法区和永生代之间关系类似于接口和实现类,永生代是方法区的实现。
元空间:整个永生代有JVM本身设置的上限,元空间用直接内存。
运行时常量池
Class文件除了有类的版本、字段、方法、接口等信息,还有常量池表(用于存放编译器生成的各种字面量和符号引用)
直接内存
JDK1.4之后加入NIO,引入基于通道(Channel)和缓存区(Buffer)的IO方式,可以直接使用本地库分配堆外内存,然后通过Java堆中的DirectByteBuffer对象作为内存引用。

HotSpot虚拟机

对象的创建

类加载检查>分配内存>初始化零值>设置对象头>执行init方法

1、当虚拟机遇到一条new指令:首先检查这个指令参数是否能在常量池找那个定位到这个类的符号引用,并且检查引用的类是否已被加载过,解析和初始化,若无就执行类加载。

2、检查通过后,分配内存。为对象分配空间有两种方式,指针碰撞和空闲列表,具体哪种看java堆是否规整,堆是否规整由GC是否带有压缩整理功能。

指针碰撞:针对内存规整的情况下,用过的就规整,中间有分界指针
空闲列表:内存碎片化,虚拟机维护一个列表,记录可用空间
内存并发问题:CAS+失败重试 乐观锁
		    TLAB:现在Eden分配,JVM用的时候从Eden中拿,用完了或者大于分配内存时,用CAS

3、分配空间之后,初始化为零值,除了对象头都需要。保证JAVA实例字段不赋值直接用。

4、虚拟机要对对象进行必要设置,对象头中存储包括(这个对象是哪个类的实例,如何找到类的元数据,hash值,gc年代分层)

5、执行init方法,按照程序员的定义初始值。

对象的内存布局

在虚拟机中,对象在内存中的布局分为:对象头,示例数据,对齐填充。

对象头包括:存储自身运行时数据,另一部分是类型指针。

对齐部分不是必然存在,也没有特殊意义没知识虚拟机要求对象大小是8字节的整数倍,二对象头部分正好是8字节或16字节。

对象的访问定位

Java通过reference数据操作堆上的具体对象,对象的访问方式由虚拟机实现定,主要由句柄和直接指针两种。

句柄
java堆中划分内存作为句柄池,reference存储的就是句柄地址,句柄地址包括对象实例数据和类型数据的地址
直接指针
如果使用直接指针访问,java堆对象的布局需要考虑,reference存储的就是对象地址。

句柄的优点在于稳定,对象被移动只改变实例数据指针,reference本身不改。指针就是快,节省一次指针定位的时间开销。

补充

          String str1 = "str";
          String str2 = "ing";
          String str3 = "str" + "ing";//常量池中的对象
          String str4 = str1 + str2; //在堆上创建的新的对象      
          String str5 = "string";//常量池中的对象
          System.out.println(str3 == str4);//false
          System.out.println(str3 == str5);//true
          System.out.println(str4 == str5);//false
尽量避免多个字符串拼接,会重新创建对象,如果需要改变字符串,可以用StringBuffer或者StringBuilder
基本类型的包装类和常量池

Byte,Short,Integer,Long,Character,Boolean;前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象。浮点型没有实现常量池技术。

关于Integer源码
 public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
查阅规范说的是性能与资源的权衡,比较常用吧,可调节缓存正向最大值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值