深入理解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采用的实现方式。

相关文章
|
1月前
|
监控 算法 Java
深入理解Java虚拟机(JVM)的垃圾回收机制
【10月更文挑战第21天】 本文将带你深入了解Java虚拟机(JVM)的垃圾回收机制,包括它的工作原理、常见的垃圾收集算法以及如何优化JVM垃圾回收性能。通过本文,你将对JVM垃圾回收有一个全新的认识,并学会如何在实际开发中进行有效的调优。
46 0
|
5月前
|
安全 Java 编译器
Java虚拟机
【7月更文挑战第2天】
35 1
|
7月前
|
Oracle Cloud Native Java
【JVM】初步认识Java虚拟机
【JVM】初步认识Java虚拟机
54 6
|
7月前
|
存储 安全 Java
深入理解Java虚拟机(JVM)
深入理解Java虚拟机(JVM)
102 0
|
7月前
|
前端开发 Java
深入理解Java虚拟机:类加载机制
【2月更文挑战第23天】本文深入探讨了Java虚拟机(JVM)的类加载机制,包括类加载器的层次结构、类加载的过程以及双亲委派模型。通过对JVM类加载机制的理解,可以帮助我们编写更高效的Java代码。
|
存储 缓存 算法
JVM-Java虚拟机内存模型
Java内存模型在1.8之前和1.8之后略有不同,也就是运行时数据区域,请看如下图:
|
Java 索引
Java虚拟机(JVM)字节码指令表
Java虚拟机(JVM)字节码指令表
129 0
|
存储 安全 Java
|
存储 Java C++
java虚拟机
java虚拟机会将管理的内存划分为若干个不同的数据区域,如下图其中虚拟机栈、本地方法栈、程序计数器是线程隔离的,即每个线程都拥有自己的虚拟机栈、本地方法栈、程序计数器。方法区、堆这两块内存是所有线程间共享的 程序计数器 当前线程所执行的字节码的行号指示器(指示器工作时就说通过改变这个计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理等)。
1330 0