JVM 一
运行时数据区域
简单介绍:
方法区:和Java堆一样,是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等。
堆:堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,再虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
程序计数器:是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
虚拟机栈:其生命周期与线程相同。其描述的是Java方法执行的内存模型;每个方法在执行的同时都会创建一个栈帧( Stack Frame ),用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧再虚拟机栈中入栈到出栈的过程。
本地方法栈:与虚拟机栈所发挥的作用是非常相似的,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
什么是线程独享?
在运行时数据区域中,线程独享的有:程序计数器,虚拟机栈,本地方法栈。
独享就是这个线程必须有一套属于自己的,例如程序计数器,它是记录代码执行到的具体的行数,如果两个线程公共,那会乱套的。
什么是线程共享的?
线程共享的有方法区和堆
共享的含义就是线程都是在一个共同的一个堆中创建自己的对象和操作自己的对象,方法区中存放了对象的类信息,供所有的线程直接访问。
对象创建
创建对象常见指令
- new 指令的时候
- newInstance()方法
对象创建的流程
遇到了一个new Student() 先去判断有无Student类信息,如果有此类信息,直接分配内存,
如果无此类信息,则去加载类信息,再分配内存
内存分配的方式
- 指针碰撞
* 假设Java堆中内存是绝对完整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅把那个指针向空闲空间那边挪动一块与对象大小相等的距离,这种分配方式即为指针碰撞。 - 空闲列表
* 如果堆中的内存不是规整的,已经使用的内存和空闲的内存相互交错,,虚拟机就必须维护一个列表,记录上那些内存块是可用的,再分配的时候从列表中找到一块足够大的空间划分给对象实例冰更新列表上的记录,这种分配方式称为空闲列表 - ps: 选择哪种分配方式是由所采用的垃圾收集器是否带有压缩整理功能决定。因此,在使用Serial,ParNew等带有Compart过程的收集器时,系统采用指针碰撞,而CMS这种基于Mark-Sweep算法的收集器时,系统采用的分配方式时空闲列表。
对象的内存布局
-
有几部分组成
- 对象头
* 包括两部分:1. 用于存储对象自身的运行时数据(哈希码,Gc分代年龄,锁状态标 志,线程持有的锁,偏向线程ID,偏向时间戳等)
* 2.类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
* ps :如果对象是一个Java数组,那么对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但从数组的元数据信息中却无法确定数组大小。 - 实例数据
* 对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承的,还是子类中定义的都要记录。 受虚拟机分配策略参数和字段在Java源码中定义顺序的影响。HotSpot虚拟机默认的分配策略为longs/doubles,ints,shorts/chars,bytes/booleans,oops,相同宽度的字段被分配到一起。父类变量出现在子类之前。 - 对齐填充
* 并不是必然存在的,也没有特殊的含义,它仅仅七折占位符的作用。
- 对象头
-
对象的访问定位
-
- 通过句柄访问
-
- 通过直接指针访问
- Java程序通过栈上的reference数据来操作堆上的具体对象。由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该怎么去定位,访问堆中的对象的具体位置,所以对象访问方式也是取决于虚拟机实现而定的。目前主流的访问方式有使用句柄和直接指针。
优缺点:
句柄最大的好处是reference中存储的是稳定的句柄地址,在对象被移动时指挥改变句柄中实例数据指针,而reference本身不需要修改。
使用直接指针访问方式最大的好处就是速度更快,因为节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销时一项非常可观的执行成本。HotSpot是用直接指针访问的。
- Java程序通过栈上的reference数据来操作堆上的具体对象。由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该怎么去定位,访问堆中的对象的具体位置,所以对象访问方式也是取决于虚拟机实现而定的。目前主流的访问方式有使用句柄和直接指针。
- 通过直接指针访问