都知道堆内存要回收垃圾,如何在开发中使用对象来减少内存使用

存储 存储软件
堆内存我们大家都知道JVM内存是划分了一个是堆内存,一个是非堆内存,而堆内存分为了(年轻代),(老年代)这些,而非堆内存就是一个元空间了(1.8之后变更的,之前是永久代)。

[[379173]]

本文转载自微信公众号「Java极客技术」,作者鸭血粉丝。转载本文请联系Java极客技术公众号。  

堆内存

我们大家都知道JVM内存是划分了一个是堆内存,一个是非堆内存,而堆内存分为了(年轻代),(老年代)这些,而非堆内存就是一个元空间了(1.8之后变更的,之前是永久代)。

相比较来说,大家肯定都非常的熟悉分代的概念,知道对象首先应该放在哪里,然后移动到哪里,最后执行什么样子的方法来进行垃圾回收。而我们今天要说的却是如何考虑运行程序的机器内存限制下,让我们的对象更小一点就能完成我们的功能。

减少对象大小

阿粉和大家都一样,都知道对象会占用一定数量的堆内存,毕竟你新生成的对象首先就是要放到Eden区的,当Eden空间被占满的时候,出发Minor GC,存活下来的对象移动到Survivor区去,而我们想要减少内存的使用,最简单的方法就是在写程序的时候,也需要考虑对象的大小,毕竟如果说如果说以后再做CodeReview的时候,你会发现你的代码运行起来,你看JVM的时候会赏心悦目,但是代码也得好看不是?

阿粉就给大家看看最基础的Java基础实例变量的大小

图1:

 

实例变量,这是一个和对象息息相关的,一个对象一份实例变量,而实例变量的个数和实例变量的大小也就决定你在占用内存的大小。

大家可以想象一下,如果内存不够,那么有两种方式供你选择:

  • 选择一:增加百分之10的堆内存
  • 选择二:堆中的对象的大小减少百分之10

你会选择什么方式?一般情况你想选择第一种方式,但是这种方式好像不是那么的实际,你堆内存都不够了,你还想再继续增加点?那么只能你来选择第二种了。

但是再你选择了第二种方式之后,你又遇到了一个问题,减少对象的方式也是有两种方式:

  • 方式一:直接减少实例变量的数量
  • 方式二:减少实例变量的大小

其实这两种方式都可以,这个就是要取决于你在之前代码中做过什么,比如说你在之前的代码已经进行过实例变量的优化了,在写代码之前就已经考虑到这件事了,那么你肯定是只能选择第一种。

如果说你之前在代码中并没有去考虑过实例变量的大小,那么选择第一种将会是你最佳的方案。

分析对象大小

一个对象的大小,我们要把它分开,由三部分来组成,对象头、实例变量、内存补充,在32位的系统中,假设我们定义一个int i ,那么对象头在其中就要占据 4 字节,int 在对象中占用 4 字节,而如果是64位的话,那么对象头就变了,从4字节变成8字节,在这里我们就得注意一个事情了,如果说成员变量不论是否引用了其他的对象,它占用的字节始终是 4 字节。

这里我们就引入了一个概念:Shallow Size

Shallow Size

其实简单来说,Shallow Size 就是对象本身占用内存的大小,但是不包含其中引用的对象,这局话的后半段就是相对应的阿粉刚才所说的注意事项了。

而 Shallow Size 也是有针对的,就比如说是非数组类型的对象,他的大小就是对象和他所有成员变量大小的总和,

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

举个例子:

  1. public class A(){ 
  2.     private int i ; 
  3.     private boolen x; 

我们的A对象在我们New出来之后,发现,不是一个数组类型的,那么就得看成员变量,然后把成员变量加起来,是不是就等于 Shallow Size 了。

Retained Size

说了Shallow Size了,那么我们就不得不提 Retained Size了,因为阿粉在学习的时候,去专门翻找了资料,发现这都是一体的,你看这个,你发现下面还有和他有关联的,不学吧,弄不明白心里难受,那还是学习吧。

英文复制

Retained Size = 当前对象的大小+当前对象的引用大小(直接或者间接)都是

示例图:

 

图片网址如下https://www.yourkit.com/docs/java/help/sizes.jsp 里面也有解释,但是阿粉还是要解释一波。

在上图中 obj1 的 Retained Size = obj1 + obj2 + obj4 的 Shallow size

这是左边的,右边的是obj1 的 Retained Size = obj1 + obj2 + obj4 +obj3 的 Shallow size

在我们进行GC的时候,Retained Size是必不可少的,它有助于了解内存的结构(聚类)和对象子图之间的依赖关系,以及查找这些子图的潜在根源。

不过说实在的,因为JVM的存在,他自己的垃圾回收机制已经算是非常的不错了,但是因为我们在日常的业务中的需要,我们仍然需要去学习这些内容,毕竟万一在以后的实际工作中真的遇到了,你会发现你现在学的内容是非常有用的。

文献参考

 

《YouKit》 《Java性能权威指南》

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2023-12-19 21:52:51

Go垃圾回收开发

2022-04-29 08:05:06

内存堆外GC

2019-04-23 15:20:26

JavaScript对象前端

2011-08-15 16:28:06

Cocoa内存管理

2017-04-25 14:39:55

JVM内存Java

2014-12-19 11:07:40

Java

2018-08-13 09:57:15

LinuxFio硬盘性能

2017-10-20 08:52:11

内存缓存并发模式Linux

2009-12-09 17:28:34

PHP垃圾回收机制

2019-06-24 19:00:09

JavaScript内存泄漏垃圾回收

2019-08-01 08:00:04

AWS虚拟机Lightsail

2021-03-09 07:27:40

Kafka开源分布式

2015-08-27 09:46:09

swiftAFNetworkin

2021-06-09 09:36:18

DjangoElasticSearLinux

2022-06-23 08:00:53

PythonDateTime模块

2011-08-10 09:31:41

Hibernateunion

2024-01-18 08:37:33

socketasyncio线程

2022-05-17 08:25:10

TypeScript接口前端

2024-02-04 09:18:00

Python内存管理垃圾回收

2017-02-21 16:40:16

Android垃圾回收内存泄露
点赞
收藏

51CTO技术栈公众号