Dalvik虚拟机内存碎片测试分析

3 篇文章 0 订阅
1 篇文章 0 订阅

《移动App性能评测与优化》第一章1.3.3介绍了优化Dalvik内存碎片。文中列举了一段代码,可能会在GC后引起内存碎片问题,代码如下:

private Object result[] = new Object[100];
void fool(){
    for (int i = 0;i < 100; i++){
        byte[] tmp = new byte[2000];
        result[i] = new byte[4];
    }
}

文中大意是:result[]对象数组每一个对象在分配内存之前都会先分配一个临时的byte[[2000],因此实际给result[]对象数组每一个对象分配的byte[4]所在的内存地址是不连续的。当GC之后,临时对象tmp申请的内存被释放,留下了碎片化的内存。

提出问题:

1.GC之前result[]数组中每个成员的内存地址分布情况如何?

2.Dalvik GC会对内存碎片做整理么?

3.GC之后result[]数组中每个成员的内存地址分布情况如何?

准备工作:

Android Studio 3.2.1

Android Device Monitor,工具所在目录:Android\Sdk\tools\lib\monitor-x86_64\monitor.exe

[Memory Analyzer] (https://www.eclipse.org/mat/downloads.php)

程序编码:

package com.peterzhang.oom;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
    private Object result[] = new Object[10000];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void buttonOnclick(View view){
        testMemoryFragmentation();
    }

    private void testMemoryFragmentation(){
        for (int i = 0;i < 10000; i++){
            byte[] tmp = new byte[2000];
            result[i] = new byte[4];
        }
    }
}

点击运行,通过DeviceMonitor观察内存分配状态
在这里插入图片描述

Heap Allocated 大小为12.558M

通过DeviceMonitor左上角 "Dump HPROF File"导出此时的内存信息二进制文件a.hprof

此时生成的hprof文件还无法直接通过MAT工具打开需要利用AppData\Local\Android\Sdk\hprof-conv 工具转换格式,命令如下:

hprof-conv  C:\Users\112106101\Desktop\mat\nexus4-android4.0.3\a.hprof C:\Users\112106101\Desktop\mat\nexus4-android4.0.3\a-standard.hprof

备注:此时导出来的二进制内存文件除了包含app自身占用的内存,还包括了App共享的系统资源占用的内存。如果期望只过滤出App自身的内存可以使用如下命令:

hprof-conv -z exclude non-app C:\Users\112106101\Desktop\mat\nexus4-android4.0.3\1.hprof C:\Users\112106101\Desktop\mat\nexus4-android4.0.3\1-android.hprof

然后打开MAT工具,选择open a-standard.hprof

在这里插入图片描述

可以看到,MainActivity一共占用了42312byte

点击“分配对象”按钮,执行testMemoryFragmentation方法,通过DeviceMonitor观察内存分配状态
在这里插入图片描述
Heap Allocated 大小为12.786M

同样导出HPROF内存文件,并通过MAT工具打开:
在这里插入图片描述

可以看到此时MainActivity一共占用了201920byte

执行testMemoryFragmentation方法,并GC之后,内存Heap Allocated增加了0.228M,大概是220k

MainActivity占用内存增加了159608byte,约等于160k,小于DeviceMonitor显示的内存增加数据220k。

监测工具对象分配前后增加内存
DeviceMonitor220k
MAT160k

增加的内存主要是因为result[]的内存申请
在这里插入图片描述
观察result[]数组内存分配,发现内存地址不是连续的,莫非是GC之后是存在了内存碎片?

验证:去掉代码

byte[] tmp = new byte[2000];

会不会就不存在内存碎片呢?
测试后数据如图所示:
在这里插入图片描述

从图上的数据观察到此时依然有内存碎片。

那么我们就无法下结论这里看到的内存碎片是由于代码引起的。

byte[] tmp = new byte[2000];

什么是内存碎片?

内存需要像硬盘一样定期清理碎片么?当然!内存碎片分为内部碎片和外部碎片。例如

HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全.。

这部分对齐填充的内存就属于内部碎片。外部碎片更容易理解,GC发生后会出现空闲的内存不连续,这部分就是外部碎片。

从MAT分析的result[]对象内存数据可以看到,每一个result[i]占用的内存是16byte,对应的代码是

result[i] = new byte[4];

这里16byte是如何得到的呢?

对象在内存中存储布局可以分为3块区域:对象头、实例数据和对齐填充。如果对象是一个数组,那么在对象头中还必须有一块用于记录数组长度的数据。《深入理解JAVA虚拟机》

我们来分析一下result[i]占用的内存空间:

类型大小
对象头8byte
数组大小4byte
实例数据4byte
对齐填充4byte

因此每一个result[]对象就占用了4byte的内部碎片。

除了内部碎片,从MAT分析的内存数据看到,还存在一些外部碎片。这也是上述测试数据中MAT分析的result[]申请对象160k,但是实际上HeapAllocated增加了220k的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值