JVM---1.内存区域划分

第一节 概念

     java源文件,如何在计算机上运行?

    答:.java文件首先通过编译器,比如javac变成成为.class字节码文件,jvm虚拟机将.class载入内容翻译成为机器码即可运行。

    .class在载入jvm虚拟机的过程中,其实就是载入内存中,下面我们看一下jvm是如何规划内存的。

   运行时数据区,从线程使用范围进行划分为两块,左边的方法区和堆,右边的程序计数器,本地方法栈与虚拟机栈。

1.1 程序计数器

    一块较小的内存空间,当前线程执行的字节码的行号指示器。cpu里面能并行运行的线程很少,但是cpu能处理几千个线程,为什么呢?因为有时间片轮流使用cpu,所以如果没有计数器,那么这个线程下次苏醒的时候,就不知道刚才执行到哪里了。

1.2 虚拟机栈

    线程的工作平台,它的本质就是一个栈,先入后出,不断把工作需要的数据,压进去,然后弹出来,每一方法对应一个栈帧,线程总时操作最顶上的一个栈帧。它与程序计数器都是线程私有的,它描述的是java方法执行的线程内存模型,下面的图中展示了一个方法在内存的具体存储情况。

    局部变量表:存放编译器可知的各种基本数据类型(复杂的对象存放在堆中),它的大小在编译的时候,就确定在了字节码文件中。还可以存放对象的引用,32位为一个变量槽,long这种就算为两个变量槽。如果请求的量,超过了jvm规定的大小,那么就会抛出异常。

    操作数栈:

    动态连接:

    返回地址:方法开始执行之后,只有两种方式可以推出,返回值,或者抛出异常

    帧数据区:

1.3 本地方法栈

    与虚拟机栈很相似,区别只是虚拟机栈执行的是java的方法,而本地办法native栈,执行的是本地方法服务(比如c)

1.4 java堆

    堆是jvm管理的最大的一块内存区域,它是用来存放实例化对象的。而且她是线程共享的地方。一个对象存放在堆里面也是三个内容:对象头,对象数据,对齐填充(下面有图)。堆也是垃圾收集器管理的内存区域,垃圾回收也管理方法区,但是主要是搞定堆。不然堆会爆满,抛出异常。它的大小,可以通过jvm命令:-Xmx和-Xms设定。

1.5 方法区

    用来存放类被加载的元数据,也就是类型信息呀,常量呀,静态变量呀这些内容,不轻易改变的量,字节码被装载之后,就是存放在这个地方,她的代码也就是在这个地方保存。

    运行时常量池:这个也是方法区的一部分,但是我们要单独拿出来搞一下,常量池用来存放编译器生成的各种字面量和符号引用。

    直接内存:jdk1.4推出了解决阻塞Io的nio,引入了基于通道的channel,与缓冲区的io方式,它使用native函数直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样避免了java堆和native堆中来回复制数据。它不受jvm 内存大小的影响,但是他受pc内存的影响,它是交给操作系统区分配的,保存在直接内存中的数据,不用再转到内核缓存中发送,实现了零拷贝,但是它的创建和回收相对于jvm来说,就要复杂一点。

第二节 类实例创建

2.1 对象的创建

    上面时候会触发new操作?

    普通对象,不包括数组和class对象。当一个new发生的时候:

    1.先去判断在常量池中是否有符号引用,且检查它是否已经被加载,没有加载则需要启动类记载器)

    2.当类加载器完成之后,就需要给新生对象在jvm堆中分配内存空间,这个空间大小是在类加载完成后就确定了的,

    3.初始化:除了对象头,其他都初始化位0,这就是成员变量为什么有初始值。

    4.对象信息设置:标记是哪个类的实例,如何找到类的元数据,gc分代年龄信息,锁等问题。

2.2 对象的内存布局

    1.对象头:自身运行时的数据,如锁的标志,mark word  为了节约资源,提高效率这里涉及到了动态大小分配,也就是在不同情况下,一个属性的大小是不同的。

                     指针,指向类型元数据的指针,确定是哪一个类的实例化对象,(但是查找元数据不一定通过对象头的指针),如果对象是一个数组,则对象头必须存放数组长度的数据。

    2.实例数据:对象的有效信息,父类中继承的等等,所有字段内容。它的顺序收class文件定义顺序和jvm分配策略影响,(因为重排序,会把一些相同类型的放在一起,便于内存管理和存放,节约空间)

    3.对齐填充:由于jvm规定:存在必须是8字节的倍数,所以需要填充,占位符的作用。

2.3 对象的访问定位

     线程如何找到堆中的对象呢?使用的是栈上的reference数据来操作,(栈的本地变量表中:基本数据类型和reference)

        1.句柄池:在堆中,开辟一个区域存放句柄地址。一个是实例的地址,一个是类型的地址(指向方法区)

        2.直接指针访问:reference直接指向堆中的对象地址,如果要访问类型地址,则需要通过对象实例对象头中的信息进行跳转到方法去的地址。

    两种方法各有优点,第一个在堆变化之后,只需要修改句柄池,不需要修改reference,而第二种就访问的更开,在hotSpot使用的是第二种方式实现的,直接指针访问更快,因为减少了一次指针定位的开销。

参考书籍:《深入了解java虚拟机》

栈帧结构: https://www.cnblogs.com/jhxxb/p/11001238.html

栈运行流程:https://blog.csdn.net/a15089415104/article/details/83245568

内存划分:https://blog.csdn.net/Zz110753/article/details/70170339

执行引擎:https://blog.csdn.net/xiaoHui_1126/article/details/104889518?fps=1&locationNum=2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值