java堆中句柄池_面试题:JVM在Java堆中对对象的创建、内存结构、访问方式

3f4ae0eb0d7d1dcd96bc483a20da725b.png

一、对象创建过程

7c54cba97053b09bc7f635e2ccb1930c.png

1、检查类是否已被加载

JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载、解析、初始化,若没有,则进行类加载

2、为新对象分配内存

类加载检查后,JVM为新对象在堆内存中分配空间,内存大小在类加载完成后便可确定。内存分配方式有以下几种:

1)指针碰撞(Bump the Pointer):若堆内存规整的,已用的和空闲的各占一边,分配内存就是把指针作为分界点,指针往空闲的一边移动对象大小的空间。

2)空闲列表(Free List):若堆内存不规整,JVM必须维护一个记录可用内存块的列表,分配内存时,把列表中一块空间分配给对象,并更新表记录。

以上两种在并发情况下,存在线程安全问题,在给对象A分配内存时,指针还没来得及修改,对象B又同时使用原来的指针来分配内存。解决方案有两种:

1)给分配内存的动作同步处理:JVM使用CAS+失败重试,保证更新操作的原子性。

2)本地线程分配缓冲(TLAB Thread Local Allocation Buffer):给每个线程在堆内存中预先分配已小块内存,在需要分配内存的线程的TLAB上分配,TLAB用完并分配新的TLAB时,才同步锁定。JVM通过设置 -XX:+UseTLAB来开启。

3、将分配到的内存都初始化为零值(不含对象头)

保证了对象的实例字段在java代码中不赋初始值就可以直接使用。如果使用TLAB,这一步可提前到TLAB分配时进行。

4、对对象进行其他必要的设置

如设置对象头的内容

5、执行java代码中方法进行初始化

以上4步完成后,对于JVM来说,新的对象已经产生了,但是对于java程序来说,对象才刚刚开始创建。

二、对象的内存结构

8a2adfc685345e52062dc241049f56d4.png

1、对象头

1.1 标识字段 Mark Work

用于存储对象自身的运行时数据,如HashCode,GC分代年龄,锁状态标志等

1.2 类型指针 Klass Pointer

对象指向它的类型元数据的指针,JVM通过这个指针确定该对象属于哪个类的实例

如果对象是一个数组,对象头中还要有一块用于记录数组长度的数据,因为数组长度是不确定的,无法通过元数据中的信息推断数组大小。

584afbe075ea3c191abff147164977e1.png

2、实例数据

对象实际存储的有效信息,即代码中定义的字段和父类继承下来的,存储顺序受到JVM分配策略参数(-XX:FieldAllocationStyle)和代码中字段定义顺序影响

3、对齐填充

不是必然存在,仅仅是起占位符作用;由于HotSpot虚拟机的自动内存管理系统要求任何对象大小都必须是8字节的整数倍,对象头被设计成正好是8字节的整数倍,因此实例数据部分没有对齐8字节的整数倍的话,就通过对齐填充来补全。

三、对象的访问方式

9776588cff930e96dc6e2838cbe1c18c.png

java程序是通过java栈中的reference数据来操作堆中的具体对象

1、句柄访问

java堆中划分一块内存作为句柄池,栈上的reference存的是对象的句柄地址,句柄池中包含对象实例数据和类型数据的地址信息。

优点:垃圾收集移动对象时,只改变句柄中实例数据指针,而reference本身不需要修改。

0e60e262d0292eb4045244343b0f14ec.png

2、直接访问

直接指针访问,reference存的直接是对象的地址。不需要多一次间接访问的开销。

优点:速度快,节省一次指针定位的时间开销。

HotSpot虚拟机主要使用直接访问进行对象访问。

49ae1c0eccc2377e8a025ee97461580a.png

参考文献:

1.《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 C 语言代码示例,演示如何在两个进程使用 `memfd_create` 创建的文件句进行进程间通信: ```c // 进程 A #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { // 在进程 A 创建一个匿名的文件描述符 int memfd = memfd_create("sharedmem", 0); if (memfd == -1) { perror("memfd_create"); return 1; } // 设置文件描述符的大小 if (ftruncate(memfd, 4096) == -1) { perror("ftruncate"); return 1; } // 在进程 A 映射该文件描述符到内存 void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); if (addr == MAP_FAILED) { perror("mmap"); return 1; } // 在进程 A 写入数据到共享内存 sprintf((char*)addr, "Hello from process A"); // 将文件描述符的值发送给进程 B printf("Sending file descriptor %d to process B...\n", memfd); if (write(STDOUT_FILENO, &memfd, sizeof(memfd)) == -1) { perror("write"); return 1; } // 等待进程 B 读取共享内存的数据 printf("Waiting for process B...\n"); sleep(5); // 取消映射文件描述符 if (munmap(addr, 4096) == -1) { perror("munmap"); return 1; } // 关闭文件描述符 if (close(memfd) == -1) { perror("close"); return 1; } return 0; } ``` ```c // 进程 B #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { // 从进程 A 读取文件描述符的值 int memfd; printf("Waiting for file descriptor from process A...\n"); if (read(STDIN_FILENO, &memfd, sizeof(memfd)) == -1) { perror("read"); return 1; } // 在进程 B 创建一个匿名的文件描述符 int memfd2 = memfd_create("sharedmem2", 0); if (memfd2 == -1) { perror("memfd_create"); return 1; } // 设置文件描述符的大小 if (ftruncate(memfd2, 4096) == -1) { perror("ftruncate"); return 1; } // 在进程 B 映射该文件描述符到内存 void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, memfd2, 0); if (addr == MAP_FAILED) { perror("mmap"); return 1; } // 在进程 B 从共享内存读取数据,并将其打印出来 printf("Message from process A: %s\n", (char*)addr); // 在进程 B 写入数据到共享内存 sprintf((char*)addr, "Hello from process B"); // 取消映射文件描述符 if (munmap(addr, 4096) == -1) { perror("munmap"); return 1; } // 关闭文件描述符 if (close(memfd) == -1) { perror("close"); return 1; } if (close(memfd2) == -1) { perror("close"); return 1; } return 0; } ``` 这个示例,进程 A 和进程 B 都使用 `memfd_create` 创建了一个匿名的文件描述符,并将其映射到内存。进程 A 向进程 B 发送文件描述符的值,进程 B 接收到文件描述符后,创建一个新的匿名文件描述符,并将其映射到内存。进程 B 从共享内存读取进程 A 写入的数据,并将自己的数据写入到共享内存,然后关闭文件描述符。最后,进程 A 等待进程 B 处理完共享内存的数据,然后也关闭文件描述符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值