【JVM原理探索】Java各种类型对象占用内存情况分析

简介: 【JVM原理探索】Java各种类型对象占用内存情况分析

前言


只有当你到了一定层次,需要了解JVM内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时。





内存公式


Java对象的内存布局=对象头(Header)+实例数据(Instance Data)+补齐填充(Padding)




补齐填充


Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数




Shallow Size


  1. 对象自身占用的内存大小,不包括它引用的对象。


  1. 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。


  1. 针对数组类型的对象,它的大小是数组元素对象的大小总和。




Retained Size


Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C,C就是间接引用)


换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存


不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。


image.png




接下来用JProfiler验证:


  1. 新建一个空对象,观察空对象内存占用
public class TestObject {}
复制代码


对象占用内存 16byte,如图:

image.png


结论


一般自建空对象占用内存 16Byte,16byte = 12Byte(Header) + 4Byte(Padding)

  1. 在TestObj中新增一个int属性,观察对象内存占用
public class TestObj {
    private int i;
}
复制代码


对象占用内存16byte,如图

image.png


结论


int 占用 4byte,16byte = 12byte(Header) + 4byte(int)+0byte(padding)

  1. 在TestObj中新增一个 long 属性,观察对象内存占用
public class TestObj {
    private long i;
}
复制代码

对象占用内存 24b,如图

image.png

结论


long 占用 8byte, 24byte = 12(Header) + 8(long) + 4(Padding)

其余基本类型可以参照以上自行验证,原理一样




包装类型占用


  • 包装类(Boolean/Byte/Short/Character/Integer/Long/Double/Float)占用内存的大小 = 对象头大小 + 底层基础数据类型的大小
  • 包装类和其他引用类一样,会产生一个引用(reference)


image.png

  1. 在TestObj中新增一个Integer属性,观察对象内存占用
public class TestObj {
   private Integer  i =128;
}
复制代码

对象占用内存 32b,如图

image.png



结论


Integer 占用16b, 32 = 12 (Header) + 16(Integer) + 4(reference)


特别的:-128~127在常量池,只占用4b,且不产生引用(reference)

  1. 在TestObj中新增一个 Long属性,观察对象内存占用
public class TestObj {
   private Long  l = new Long(1);
}


image.png




结论


Long 占用 24b, 40 = 12 (Header) + 24(Long) + 4(reference)

其余包装类型可以参照以上自行验证,原理一样



基本类型数组占用


64位机器上,数组对象的对象头占用24 bytes,启用压缩后占用16字节。比普通对象占用内存多是因为需要额外的空间存储数组的长度(普通16b-12b)


对象数组本身的大小=数组对象头 + length * 存放单个元素大小

在TestObj中新增一个 char[] 属性,观察对象内存占用

public class TestObj {
   private char[] c = {'a','b','c'};
}
复制代码


char[] c占用内存 40b,如图

image.png



结论


char[3] 占用 24b, 24 = 40 - 16,24 = 16(Header) + 3 * 2(char) + 2(Padding)




封装类型数组占用


封装类型数组比基本类型的数组,需要多管理元素的引用


对象数组本身的大小=数组对象头+length * 引用指针大小 + length * 存放单个元素大小。



在TestObj中新增一个 Integer[] 属性,观察对象内存占用。

public class TestObj {
    private Integer[] i = {128,129,130};
}
复制代码

Integer[] i占用内存 80b,如图

image.png



结论


Integer[3] 占用 80b, 80 = 96 - 16, 80 = 16(Header) + 3 * 4 (reference)+ 3 * 16(Integer) +4(padding)





String占用内存


在TestObj中新增一个空String属性,观察对象内存占用。

public class TestObj {
    private String s = new String("");
}
复制代码

对象占用内存 40b,如图


image.png




结论


String本身占用24byte,24=40-16,也就是说空"",也需要16byte





注意


这里为什么要写String s = new String("")?请自己思考,不写会怎么样?


答:如果写成String s = “”,是不会再堆中开辟内存的,也就看不到String占用的空间,你看到的将会是下面的,至于为什么,都是因为final。


image.png
















相关文章
|
15天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
11天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
11天前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
13天前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
Java ArrayList扩容的原理
|
11天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
33 6
|
10天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
15天前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
35 2
|
11天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
7天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
28 9
|
10天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####