深入理解java虚拟机(五)

简介: 深入理解java虚拟机(五)

一、Java虚拟机栈不存储运行时的数据,

比如声明一个String a="abc";,这个变量是在方法区还是堆呢?

如果存放在堆的话,这时就在堆里面分配一块内存,如果在声明一个String b="abc"

如果存放堆的话,也会在堆里面分配一块内存。

a==b;//true

如果a,b变量都在堆里面分配内存的话,就为false,所以常量的分配内存不是在堆里面分配的

而是在方法区里面分配的。方法区里面采用StringTable(HashSet)维护的。

如果存放了abc的话,b就存放布进去了。这时a与b就相等了。

String c=new String("abc");

c.intern()==a;//true

intern():方法的作用将内存地址移动到方法区的常量池里面

二、你的对象是怎么什么来的?

简介:java程序员不可不知的对象创建地测才能够步骤细节

1、对象创建的流程步骤包括哪些:比如 A a=new A();

    ①、虚拟机遇到一条new指令时,首先检查这个对应的类能否在常量池中定位到一个类的符号引用。如果能定位到的话,这个类就是在常量池里面存在的。

init方法:static修饰的方法/代码块。

     ②、判断这个类是否已被加载 ,解析和初始化(加载的步骤))

     ③、为这个新生对象在java堆中分配内存空间,其中java堆分配内存空间的方式主要有以下两种:几乎所有的对象和数组的创建都是在Java堆里面创建的。

A、指针碰撞

在上面的图代表堆内存。FREE代表的是空闲内存,USE代表已经使用内存。

当为对象分配一块内存的时候,会将使用的内存扩大,将空闲的内存缩小

中间的部分就是对象所分配的内存。将这种分配内存的方式称之为指针碰撞。

    总结:Ⅰ、分配内存空间包括开辟一块内存和移动指针两个步骤

               Ⅱ、两个步骤可能会非原子步骤可能出现并发问题,java虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。java虚拟机采用的是乐观锁的方式会进行cas的判断比如这边有两个步骤,在维护空闲列表的时候,就可以进行cas的判断预期值和插入的值是否进行匹配,如果匹配的话,就可以操作如果不匹配,就进行重试,这就是cas的乐观锁的原理。

B、空闲列表

java堆内存对象的内存分布,有可能是不均匀的。比如说内存分布非常的乱的时候,就无法通过指针碰撞的时候给对象分配内存。称这种方式为空闲列表:存储堆内存的分配的空闲的地址

在Free区可能内存是一块一块的,空闲列表所维护的就是不规则,不连续的内存。这几块内存就可以通过空闲列表进行维护,维护的就是空闲内存的地址。如果当某个地址被用的时候,就可以从空闲列表里面剔除。

          总结:  Ⅰ、分配内存空间包括开辟一块内存和修改空闲列表的两个步骤

                     Ⅱ、两个步骤可能会非原子步骤可能出现并发问题,java虚拟机采用CAS配上失败重试的方式来保证更新操作的原子性。java虚拟机采用的是乐观锁的方式,会进行cas的判断,比如这边有两个步骤,在维护空闲列表的时候,就可以进行cas的判断预期值和插入的值是否进行匹配,如果匹配的话,就可以操作,如果不匹配,就进行重试,这就是cas的乐观锁的原理。

2、将分配的内存空间都初始化为零值:java虚拟机自己帮做的

3、设置对象相关的数据

       GC分代年龄

       对象的哈希码hashcode

       元数据信息

4、执行对象的<init>方法,比如static方法/代码块是在创建对象之前创建的。


三、java对象内存布局:

A a1=new A();在创建对象的时候,会在堆中给这个对象A开辟一块内存。

那么这个A对象里面包含那几个部分呢?

数据类型,对象的属性,属性的值称之为实例数据

而且所有的类都继承Object类,hashCode的值也是属于对象里面的一部分。

hashcode肯定是存储在内存里面。

锁状态标识:代表当前对象是否持有锁  

GC分代年龄:java虚拟机要对垃圾进行回收,将所回收的垃圾分成了新生代和老年代

java虚拟机在进行回收的时候就会分不同的年代,采用不同的回收的策略,回收算法进行回收

基本的回收算法有三种:标记清除,标记整理,复制算法。那么在不同的GC年代里面采用的回收算法就不一样。GC分代年龄就是在垃圾回收的时候所设置的概念。

锁状态:就是多线程在操作这个对象的时候,为了保证线程安全,所设置的概念。

比如说当前的线程占用了这把锁标识了这把锁,就是锁占用的状态。

线程持有的锁:就是这个线程所占有锁的指针。

对象头还有一个名词是 Mark Word

类型指针:为了定位在方法区里面是哪个类,指向方法区

对象头是在64位的系统里面,占的内存就是64位,如果在32位系统里面占用的内存是32位。

64位和32位都是8的倍数,对象在存储的时候会保持一种原则:保持8的倍数。

对其填充:存储的是对象的数据是占位符,因为对象头Header已经保证了8的倍数,实例数据无法保证8的倍数,那么对齐填充,为了实例数据保持8的倍数。

四、你是怎么访问你的对象的

简介:java程序员不可不知的对象访问定位方式

1、当我们在堆上创建一个对象实例后,就要通过虚拟机栈中的reference类型数据操作堆上的对象。现在主流大的访问方式有两种(Hotspot虚拟机中采用的是第二种)

    ①、使用句柄访问对象,即reference中存储的是对象 句柄的地址,而句柄中包含了对象实例数据与类型数据的具体的地址信息,相当于二级指针

对应另外一种访问是对象句柄访问

句柄访问是在堆里面开辟一块内存,用来存储句柄池:就是存储访问句柄的池子

也就是存储访问的指针。它会存储对象类型是数据的指针,和对象实例数据的指针。

    ②、直接指针访问对象,即reference中 存储的就是对象 地址,相当于一级指针。

写个main方法,main方法是对应java虚拟机中的栈

在方法栈的本地变量表里面,做一个对象的访问:User  user=new User();

user.getId();

对User对象的访问主要访问的是,刚才写到在对象里面存储的是User对象的实例数据

除了实例数据还有对象的类型数据,下面的是实例数据

对象类型数据:Class文件有哪些类型,实例,实现的方法,接口,这些数据称之为对象的类型数据。

对象的访问就是访问它的对象的实例数据和对象的类型数据。

直接指针访问:局部变量表是在java栈里面的,里面存储对对象的访问的指针reference

通过reference就可以对应到堆上面的对象的实例数据,那么在堆里面也会存储对象的类型数据的指针,通过这个指针指向方法区的对象类型数据(Class文件肯定是放到方法区里面的)。

堆会存放对象类型数据的指针,和对象的实例数据。

直接访问就是对对象实例数据和对对象类型数据的指针的访问。


对比:

  垃圾回收分析:方式1当垃圾回收移动对象时,reference中存储的地址是稳定的地址,不需要修改,仅仅需要修改对象句柄的地址;方式2垃圾回收需要修改reference中的存储地址

   访问效率分析:方式二优于方式一,因为方式二只进行了一次指针定位,节省了时间开销,而这也是Hotspot采用的实现方式。

相关文章
|
4月前
|
安全 Java 编译器
Java虚拟机
【7月更文挑战第2天】
30 1
|
6月前
|
Oracle Cloud Native Java
【JVM】初步认识Java虚拟机
【JVM】初步认识Java虚拟机
48 6
|
6月前
|
存储 安全 Java
深入理解Java虚拟机(JVM)
深入理解Java虚拟机(JVM)
70 0
|
6月前
|
前端开发 Java
深入理解Java虚拟机:类加载机制
【2月更文挑战第23天】本文深入探讨了Java虚拟机(JVM)的类加载机制,包括类加载器的层次结构、类加载的过程以及双亲委派模型。通过对JVM类加载机制的理解,可以帮助我们编写更高效的Java代码。
|
存储 Java
JVM系列(2):Java虚拟机栈
 栈也叫栈内存,主管 Java 程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就 Over,生命周期和线程一致,是线程私有的。8 种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配。
48 0
|
存储 监控 前端开发
深入理解JVM虚拟机 - JVM的初步了解
​ 这是一篇JVM的基础篇章,大致内容为讲解JVM的入门以及初级知识,重点在于关注JVM在日常运行中充当的角色以及如何加载一个Java程序直到程序结束的整个流程梳理。
121 0
|
存储 缓存 算法
JVM-Java虚拟机内存模型
Java内存模型在1.8之前和1.8之后略有不同,也就是运行时数据区域,请看如下图:
|
Java 索引
Java虚拟机(JVM)字节码指令表
Java虚拟机(JVM)字节码指令表
125 0
|
存储 安全 Java