art 笔记

《深入理解art 》 --邓凡平  

参考书籍:

https://blog.csdn.net/xiaolli/article/details/108095086

art/libartbase/base/logging.h

art/dalvikvm/dalvikvm.cc

 dalvikvm(int argc, char** argv) -> JniInvocation() -> JniInvocationCreate()

app_main.cpp

frameworks/base/core/jni/AndroidRuntime.cpp

AndroidRuntime::start -> JniInvocation() -> JniInvocationCreate()

AndroidRuntime::start(){

jni_invocation.Init(NULL);

startVm(&mJavaVM, &env, zygote, primary_zygote)

}

jni_invocation.Init里面根据参数库打开对应库(Null 就使用默认libart.so),如果失败就从persist.sys.dalvik.vm.lib.2 获取库打开。在从打开库里面找到JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs 三个函数链接符号,否则就失败。这三个函数定义在java_vm_ext.cc 中

AndroidRuntime::start(){

    1、设置虚拟机参数

2、JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs)

这里跟从libart.so 获取的JNI_CreateJavaVM 同名函数不是同一个,获取的为JNI_CreateJavaVM_ 指针,最后通过jniinvocation调用到libart.so 获取的指针

    JniConstants::Uninitialize() -> JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args)

}

JavaVm 就是JNIInvokeInterface 这个类,其中几个重要方法是

  1. 将JavaVM(this) 与当前线程绑定
  2. 与当前线程解除绑定
  3. 获取JniEnv
  4. 销毁JavaVm

JniEnv 就是JNINativeInterface类就是反射调用java方法接口

startVm走到libart.so 的JNI_CreateJavaVM 方法中
/art/runtime/java_vm_ext.cc

/art/runtime/runtime.cc

/art/runtime/thread.h

/art/runtime/jni_env_ext.cc

A:

  1. 创建Runtime实例,全局单实例,否决返回错误;Runtime.Create()创建单实例,执行Runtime.Init();java.lang.runtime.java 是跟native runtime对应

runtime_options.def  定义了art 虚拟机需要的参数信息

  1. 在Runtime.init(options) 中回有跟启动库,信号处理函数相关、GC 相关、Heap 相关;

java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);创建JavaVMExt 虚拟机,同时与Runtime关联上了;

Runtime::init(){

1、参数处理

MemMap::Init();

创建一页匿名”Sentinel fault page”

ebad6000-ebad7000 ---p 00000000 00:00 0 [anon:dalvik-Sentinel fault page]

Heap::heap()

java_vm_ = JavaVMExt::Create

.....

}

3、执行Thread::Startup();

   执行Thread* self = Thread::Attach("main", false, nullptr, false);这里会创建Thread,并执行Thread.Init,在Init 里面会创建JNIEnvExt,并与Java_vm 和Thread 关联起来,并会将jni_env 保存在线程独有变量中,下次可以直接获取。

B:

Runtime->start(),启动Java vm;复制java vm 和jni_env

*p_env = Thread::Current()->GetJniEnv();*p_vm = runtime->GetJavaVM();

虚拟机主线程,对应的JNIEnvExt 是Runtime 里面创建的,否则是线程创建。

Allocator.h 中的AllocatorTag 定义了多钟art内存分配跟踪

Thread Safety Annotations and Analysis (aka Annotalysis)

ImageSpace  创建

https://blog.csdn.net/weixin_33928467/article/details/89614535

Runtime::start()

Heap

使用new 和alloc 分配的对象得到的是该对象的地址,为4 个字节。

HeapBitMap 就是使用一位来表示对应的指针指向的对象是否空闲,同时通过偏移来访问指针对应对象地址,从而节省空间;也可以用一位指示一块区域(如4K)是否空闲

class HeapBitmap {

// Bitmaps covering continuous spaces std::vector<ContinuousSpaceBitmap*,             TrackingAllocator<ContinuousSpaceBitmap*, kAllocatorTagHeapBitmap>>     continuous_space_bitmaps_;

 // Sets covering discontinuousspaces.

std::vector<LargeObjectBitmap*,           TrackingAllocator<LargeObjectBitmap*, kAllocatorTagHeapBitmapLOS>>     large_object_bitmaps_;

}

SpaceBitMap 是一个模板类,ContinuousSpaceBitmap 是按对象(8byte)对齐;

LargeObjectBitmap是按页(4K)对齐;

Heap_Bitmap 有两个成员变量

// Bitmaps covering continuous spaces.

  std::vector<ContinuousSpaceBitmap*,

              TrackingAllocator<ContinuousSpaceBitmap*, kAllocatorTagHeapBitmap>>

      continuous_space_bitmaps_;

  // Sets covering discontinuous spaces.

  std::vector<LargeObjectBitmap*,

              TrackingAllocator<LargeObjectBitmap*, kAllocatorTagHeapBitmapLOS>>

      large_object_bitmaps_;

art的bitmap使用_jamousjang的博客-CSDN博客

ART中创建的Space类型由ART 启动参数决定的,Heap 对象会记录所有创建的Space。

ImageSpace 

ImageSpace 存储art 文件

包含Dex jar 或apk  文件经dex2oat 编译得到oat 和art 文件

其中dex 文件被完整拷贝到oat 里面

Art 文件就是虚拟机中 image ,art 文件与oat 文件密切相关,art 文件通过mmap 文件映射到内存。

来自 system/framework 下面核心jar 包art 文件 boot img ,来自app 对应apk 中 art 文为app image

针对系统核心都会编译生成art ,app 则根据是通过odex 来控制是否生成art

Art 文件有9个段,第0 个段保存ImageHeader 地址,最后一个段是按照4K 对齐后的地址

一个art 文件通过mmap 映射对应其内容,另外ImageSpace 通过位图来管理内存,从而会再通过mmap 创建一个内存保存位图;

虚框MemMapSpace 是基类成员,其mem_map_ 指向art所有内容(ImageHeader),除了位图对应mmap 外,begin_ 和end_ 只对应art 内容

虚框ContinuousSpace 是基类成员

ClassLinker 就是管理类关联,

::InitFromBootImage 则会赋值ClassLinker  class_root_ ,每个对象都是属于某个class 类型对象

ObjectArray<Class>

ObjectArray<DexCache>

Dlmalloc

GC Roots包括: 1). 虚拟机栈中引用的对象。 2). 方法区中类静态属性实体引用的对象。 3). 方法区中常量引用的对象。 4). 本地方法栈中JNI引用的对象。

垃圾回收算法:

Android GC - 知乎

标记-清除算法(Mark-Sweep) 会产生碎片

标记-压缩法  

复制算法(Copying)   内存使用率低

分代收集算法(Generational Collection)  Java 对分为老年代和新生代,新生代使用复制算法,老年代使用标记-清除或标记-压缩算法

Concurrent Mark-Sweep GC(CMS)

https://www.jianshu.com/p/f91a05299c2b#comments

Semi-Space GC    ----对新生代进行

https://www.jianshu.com/p/6b91a25e7dad

Generational Semi-Space GC

Mark-Compact GC

ART运行时CompactingGC为新创建对象分配内存的过程分析 - 爱问共享资料

Clang 静态线程安全:深海游弋的鱼 – 默默的点滴

concurrent collectors [CMS(Concurrent Mark Sweep)]并发垃圾回收器

GC root:标记算法我们可以了解为一个可达性算法

所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。

VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。

 

 

Cms 标记算法

GC之详解CMS收集过程和日志分析_无梦者追梦-CSDN博客

垃圾回收:触发垃圾回收的原因类型及垃圾回收器类别

collector::GcType Heap::CollectGarbageInternal 进行垃圾回收

垃圾收集器会在Heap的构造函数中被创建,然后添加到garbage_collectors_列表中。尽管各种垃圾回收器算法不一定,但它们都包含相同的垃圾回收步骤,垃圾回收器的回收过程主要包括下面四个步骤:

MarkPhase  

Live 即当前进程中所有的对象

Mark 就是标记时可以被扫描到的对象,垃圾对象为 Live集合-Mark集合

PausePhase

ReclaimPphase

FinishPhase

JVM详解 --- 垃圾回收机制

JVM详解 --- 垃圾回收机制 - 简书

https://www.jianshu.com/p/4ed43c2e00fc

Compacting GC

引进Compacting GC 后,ART运行时增加了运行时线程局部分配缓冲器(ThreadLocal Allocation Buffer)和在OOM前进行一次同构空间压缩(Homogeneous Space Compact),前者解决堆分配效率,后者解决碎片问题。

Non-Moving Space 的对象,主要包括类加载过程中创建的类对象(Class)、类方法(ArtMethod)和类成员(ArtField)等,以及那些结果若干次Generational Semi-Space GC 之后任然存活的对象。前者是通过AllocNonMovableObject接口分配,后者是在执行Generational Semi-Space GC过程移动过去的。

分配类型是由当前GC 收集器类型确定的,当发生GC收集器变化,也会改变当前分配堆内存的类型及内存不足时收集GC粒度。

 

Native内存泄漏

https://juejin.cn/post/7013595058125406238#heading-22

https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md

JNI ERROR (app bug): global reference table overflow (max=51200) 

https://blog.csdn.net/a396604593/article/details/105771093

Global reference table overflow - 简书

Heap dump 接口

 export SOONG_CONFIG_art_module_source_build=true

libcore/dalvik/src/main/java/dalvik/system/VMDebug.java

frameworks/base/core/java/android/os/Debug.java

art/runtime/native/dalvik_system_VMDebug.cc

static JNINativeMethod gMethods[] = {

      NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),

static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {

  ...

  hprof::DumpHeap(filename.c_str(), fd, false);

}

art/runtime/hprof/hprof.cc

void ProcessBody() REQUIRES(Locks::mutator_lock_) {

........

runtime->VisitRoots(this);

//获取下面的root

runtime->VisitImageRoots(this);

//这里从ImageSpace 里面获取root

auto dump_object = [this](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {

      DCHECK(obj != nullptr);

      DumpHeapObject(obj);

};

runtime->GetHeap()->VisitObjectsPaused(dump_object);

//这里获取RegionSpace 和其它区域

art\runtime\class_linker.h

继承 ClassFuncVisitor 得到所有加载的class

art/runtime/native/dalvik_system_VMDebug.cc

class_linker.cc

各个art 参数定义

art/runtime/runtime_options.def#:~:text=art/runtime/runtime_options.def

Define("-XX:GlobalRefAllocStackTraceLimit=_") // Number of free slots to enable tracing.

Hprof 分析

Java内存泄漏分析系列之六:JVM Heap Dump(堆转储文件)的生成和MAT的使用 - LeoWang, - 博客园

art/runtime/gc/accounting/     

Bitmap/card_table

art/runtime/gc/allocator/

art/runtime/gc/collector/

art/runtime/gc/space/

art/runtime/gc/base

Lock/mute

art/runtime/hprof/

art/perfetto_hprof/

      

一个解析工具源码

GitHub - badoo/hprof-tools: Tool for deobfuscating memory dump files

Hprof 二进制文件字段说明

http://hg.openjdk.java.net/jdk6/jdk6/jdk/raw-file/tip/src/share/demo/jvmti/hprof/manual.html

https://java.net/downloads/heap-snapshot/hprof-binary-format.html

JDK  heapdump 源码

openjdk/heapDumper.cpp at 60b7a8f8661234c389e247942a0012da30146a57 · unofficial-openjdk/openjdk · GitHub

Jdk 源码

jdk8u-jdk/src/share/demo/jvmti/hprof at master · frohoff/jdk8u-jdk · GitHub

Hprof 协议

Android Hprof 协议 - 仰简的个人空间 - OSCHINA - 中文开源技术交流社区

java - hprof文件格式 - Thinbug

Class loader

Java ClassLoad详解 - i野老i - 博客园

Outofmemory

art/runtime/runtime.cc

runtime->VisitRoots(this);

-->VisitNonConcurrentRoots 

  --> VisitThreadRoots

  --> VisitNonThreadRoots

-->VisitImageRoots

-->VisitTransactionRoots

-->VisitConcurrentRoots

MAT 工具

Shallow Size (对象自身占用的内存大小)

Retained Size (被GC后Heap上释放的内存大小)

shallow heap:对象本身的大小,如果是数组或集合则是各个元素的总大小。

retained heap:对象本身的大小 + 引用的其他对象的大小。

with outgoing references(查看对象为什么消耗内存,查看对象引用的其他对象)

with incoming references(查看对象被谁引用)

-Xms200m -Xmx200m -Xmn200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\Users\80280267\IntelliJIDEAProjects\MyApplication\JavaHprof

https://www.kancloud.cn/dztec/jvm_mon/1482108

https://www.cnblogs.com/hanlinhu/p/10174185.html

计算方法

https://blog.csdn.net/u013309870/article/details/52038407

tasklist -v | find "java"    //找到java pid

netstat -nao | find  "13212"    //找到pid对应port

jstat –gcutil ${pid} 1000 

BumpPointerSpace

BumpPointSpace 是顺序分配内存,分配方法很简单,跟ZygoteSpace一样不能释放,Free()没有实现,比较实用与线程局部内存,通过clear()内存一次性释放。

art\runtime\gc\space\bump_pointer_space.cc

BumpPoninterSpace.create 会创建4K对齐memmap 内存

objects_allocated_(0), bytes_allocated_(0)   分别表示分配了多少个mirro object 对象及占用的总字节空间

其提供了两种内存分配方法:

Alloc 用于为某个mirror object 对象分配所需要的内存,对象是8字节对齐返回对象指针

AllocNewTlab 当ART 虚拟机决定从调用线程的本地存储空间中分配内存时将调用。这里也是8字节对齐,其mirro object 和占用字节空间保存在Thread.tlsPtr_中

在Heap 类中有一个bump_pointer_sapce_变量,执行BumpPointerSpace,对应空间可以被任意线程当做TLAB使用,第一个分配的线程会创建一个main block 位于内存资源头部,尾部用main_block_size_ 指定,后续的线程的TLAB都会有一个BlockHeader 来描述。

Walk 用于遍历mirror Object 对象,并回调ObjectCallback 回调

GetBytesAllocated 返回分配字节数

RegionSpace

RegionSpace内存算法是将内存资源划分为一个个固定大小(KRegionSize)1M 的内存,每个内存块由Region 对象表示,其还和Copying Collection 算法(semispace) 相关

Region_space.h

  enum class RegionType : uint8_t {

    kRegionTypeAll,              // All types.

    kRegionTypeFromSpace,        // From-space. To be evacuated.

    kRegionTypeUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.

    kRegionTypeToSpace,          // To-space.

    kRegionTypeNone,             // None.

  };

  enum class RegionState : uint8_t {

    kRegionStateFree,            // Free region.

    kRegionStateAllocated,       // Allocated region.

    kRegionStateLarge,           // Large allocated (allocation larger than the region size).

    kRegionStateLargeTail,       // Large tail (non-first regions of a large allocation).

  };

kRegionStateFree空闲内存, kRegionStateAllocated已经分配过一些内存

当分配3.5M,需要4个Region块,第一个状态是kRegionStateLarge,后面3个是kRegionStateLargeTail,并且剩下的0.5M 也不能使用。

RegionSpace.alloc 分配跟BumpPointerSpace.alloc 分配算法一样,当Copy Collector semi 算法,可用的region 空间小于总的一半时,不在分配对象;

RegionSpace.allocTLAB 跟简单,它将一个region区域用于分配,空间小于总的一半时,不在分配对象。

Walk 用于遍历所有Object 对象

RegionSpace 比BumpPointerSpace 多了Region 这一层的管理,用于返回一个Object对象所属的Region 对象,RefToRegion()

MallocSpace->

DLMallocSpace 和RosAllocSpace

DLMallocSpace使用开源dlmalloc来提供内存分配和释放算法,libc 库中malloc 函数实现

RosAllocSpace 使用谷歌开源rosalloc 内存分配管理器

Heap::CreateMallocSpaceFromMemMap 用于分配管理MallocSpace,当kUserRosAlloc 宏默认为true,优先使用RosAlloc 算法,默认初始大小是4K

Mutator 表示内存创建和修改者,一般而言,mutator 指应用程序本省(除collector部分外),与collector 是相对应的,mutator_lock 就是ART 中控制内存分配与回收线程同步

Non_moving_space 是mallocspace 一种使用场景,跟GC 有关

Ros  run of slot

kNumOfSizeBrackets  值为42,表示42 种分配内存slot 粒度,

bracketSizes[kNumOfSizeBrackets]: [0]         ->8 字节

  1.         ->16 字节
  2.         ->8 * (index + 1)
  1.        ->1k
  2.        ->2k

numOfPages[kNumOfSizeBrackets]:[0~39] 为 1,[40]为2,[41]为4 ,每种slot 可以分配的内存资源个数,以4k为单位。

numOfSlots[kNumOfSizeBrackets] 对应slot 粒度下slot 个数

Run 就是管理numOfSlots[kNumOfSizeBrackets]  个同样的slot 粒度内存资源池

headerSizes[kNumOfSizeBrackets] 是指Run 管理同样粒度slot 的头部大小,为80 字节+ bitmap + 对齐

headerSizes[kNumOfSizeBrackets] + numOfSlots[kNumOfSizeBrackets] * bracketSizes[kNumOfSizeBrackets] = numOfPages[kNumOfSizeBrackets] * 4k

RosAlloc 中有42 个Mute 锁,分别对应不同粒度内存

RosAlloc::Alloc 当对象大小大于2KB,就会调用AllocLargeObject()->AllocPage来完成,否则调用AllocFromRun(),

这里如果内存不超过128KB,则从本地线程池中分配,即tlsPtr_.rosalloc_runs数组,包含16个元素,当tls 不够会从ros 中再分配。

LargeObjectSpace有两个继承类,分别是LargeObjectMapSpace和FreeListSpace

当分配的对象(Java基础对象和String)大于large_object_threshold_(默认12KB)时,是用该对象分配,这个没有算法,当需要分配时,调用memmap 分配。

large_objects_是一个map <*obj,LargeObject> ,即对象与其对应的memmap 映射。

Heap::ChangeCollector

内存分配类型的选择是由垃圾回收器确定的

 

Global reference table overflow - 简书

 

 

 

 

 

art\runtime\gc\space\space.h

namespace space {

class AllocSpace;

class BumpPointerSpace;

class ContinuousMemMapAllocSpace;

class ContinuousSpace;

class DiscontinuousSpace;

class MallocSpace;

class DlMallocSpace;

class RosAllocSpace;

class ImageSpace;

class LargeObjectSpace;

class RegionSpace;

class ZygoteSpace;

Art 分配内存对应的类

Art 分配内存对应类型

enum SpaceType {

  kSpaceTypeImageSpace,      ---预加载类系统对象,无alloc 接口

  kSpaceTypeMallocSpace,

  kSpaceTypeZygoteSpace,

  kSpaceTypeBumpPointerSpace,     ----Semi-Space GC ,对半GC

  kSpaceTypeLargeObjectSpace,      ----离散大于3个内存页的基本数据类型

  kSpaceTypeRegionSpace,        

};

ART中创建的Space类型由ART 启动参数决定的,Heap 对象会记录所有创建的Space。

ImageSpace 

ImageSpace 存储art 文件

包含Dex jar 或apk  文件经dex2oat 编译得到oat 和art 文件

其中dex 文件被完整拷贝到oat 里面

Art 文件就是虚拟机中 image ,art 文件与oat 文件密切相关,art 文件通过mmap 文件映射到内存。

来自 system/framework 下面核心jar 包art 文件 boot img ,来自app 对应apk 中 art 文为app image

针对系统核心都会编译生成art ,app 则根据是通过odex 来控制是否生成art

Art 文件有9个段,第0 个段保存ImageHeader 地址,最后一个段是按照4K 对齐后的地址

一个art 文件通过mmap 映射对应其内容,另外ImageSpace 通过位图来管理内存,从而会再通过mmap 创建一个内存保存位图;

虚框MemMapSpace 是基类成员,其mem_map_ 指向art所有内容(ImageHeader),除了位图对应mmap 外,begin_ 和end_ 只对应art 内容

虚框ContinuousSpace 是基类成员

ClassLinker 就是管理类关联,

::InitFromBootImage 则会赋值ClassLinker  class_root_ ,每个对象都是属于某个class 类型对象

ObjectArray<Class>

ObjectArray<DexCache>

Dlmalloc

GC Roots包括: 1). 虚拟机栈中引用的对象。 2). 方法区中类静态属性实体引用的对象。 3). 方法区中常量引用的对象。 4). 本地方法栈中JNI引用的对象。

垃圾回收算法:

Android GC - 知乎

标记-清除算法(Mark-Sweep) 会产生碎片

标记-压缩法  

复制算法(Copying)   内存使用率低

分代收集算法(Generational Collection)  Java 对分为老年代和新生代,新生代使用复制算法,老年代使用标记-清除或标记-压缩算法

Concurrent Mark-Sweep GC(CMS)

https://www.jianshu.com/p/f91a05299c2b#comments

Semi-Space GC    ----对新生代进行

https://www.jianshu.com/p/6b91a25e7dad

Generational Semi-Space GC

Mark-Compact GC





ART运行时CompactingGC为新创建对象分配内存的过程分析 - 爱问共享资料

Clang 静态线程安全:深海游弋的鱼 – 默默的点滴

concurrent collectors [CMS(Concurrent Mark Sweep)]并发垃圾回收器

GC root:标记算法我们可以了解为一个可达性算法

所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。

VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。

Cms 标记算法

GC之详解CMS收集过程和日志分析_无梦者追梦-CSDN博客

垃圾回收:触发垃圾回收的原因类型及垃圾回收器类别

collector::GcType Heap::CollectGarbageInternal 进行垃圾回收

垃圾收集器会在Heap的构造函数中被创建,然后添加到garbage_collectors_列表中。尽管各种垃圾回收器算法不一定,但它们都包含相同的垃圾回收步骤,垃圾回收器的回收过程主要包括下面四个步骤:

MarkPhase  

Live 即当前进程中所有的对象

Mark 就是标记时可以被扫描到的对象,垃圾对象为 Live集合-Mark集合

PausePhase

ReclaimPphase

FinishPhase

JVM详解 --- 垃圾回收机制

JVM详解 --- 垃圾回收机制 - 简书

https://www.jianshu.com/p/4ed43c2e00fc

Compacting GC

引进Compacting GC 后,ART运行时增加了运行时线程局部分配缓冲器(ThreadLocal Allocation Buffer)和在OOM前进行一次同构空间压缩(Homogeneous Space Compact),前者解决堆分配效率,后者解决碎片问题。

Non-Moving Space 的对象,主要包括类加载过程中创建的类对象(Class)、类方法(ArtMethod)和类成员(ArtField)等,以及那些结果若干次Generational Semi-Space GC 之后任然存活的对象。前者是通过AllocNonMovableObject接口分配,后者是在执行Generational Semi-Space GC过程移动过去的。

分配类型是由当前GC 收集器类型确定的,当发生GC收集器变化,也会改变当前分配堆内存的类型及内存不足时收集GC粒度。

Native内存泄漏

https://juejin.cn/post/7013595058125406238#heading-22

https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md

JNI ERROR (app bug): global reference table overflow (max=51200) 

https://blog.csdn.net/a396604593/article/details/105771093

Global reference table overflow - 简书

Heap dump 接口

 export SOONG_CONFIG_art_module_source_build=true

libcore/dalvik/src/main/java/dalvik/system/VMDebug.java

frameworks/base/core/java/android/os/Debug.java

art/runtime/native/dalvik_system_VMDebug.cc

static JNINativeMethod gMethods[] = {

      NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),

static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {

  ...

  hprof::DumpHeap(filename.c_str(), fd, false);

}

art/runtime/hprof/hprof.cc

void ProcessBody() REQUIRES(Locks::mutator_lock_) {

........

runtime->VisitRoots(this);

//获取下面的root

runtime->VisitImageRoots(this);

//这里从ImageSpace 里面获取root

auto dump_object = [this](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {

      DCHECK(obj != nullptr);

      DumpHeapObject(obj);

};

runtime->GetHeap()->VisitObjectsPaused(dump_object);

//这里获取RegionSpace 和其它区域

art\runtime\class_linker.h

继承 ClassFuncVisitor 得到所有加载的class

art/runtime/native/dalvik_system_VMDebug.cc

class_linker.cc

各个art 参数定义

art/runtime/runtime_options.def#:~:text=art/runtime/runtime_options.def

Define("-XX:GlobalRefAllocStackTraceLimit=_") // Number of free slots to enable tracing.

Hprof 分析

Java内存泄漏分析系列之六:JVM Heap Dump(堆转储文件)的生成和MAT的使用 - LeoWang, - 博客园

art/runtime/gc/accounting/     

Bitmap/card_table

art/runtime/gc/allocator/

art/runtime/gc/collector/

art/runtime/gc/space/

art/runtime/gc/base

Lock/mute

art/runtime/hprof/

art/perfetto_hprof/

      

一个解析工具源码

GitHub - badoo/hprof-tools: Tool for deobfuscating memory dump files

Hprof 二进制文件字段说明

http://hg.openjdk.java.net/jdk6/jdk6/jdk/raw-file/tip/src/share/demo/jvmti/hprof/manual.html

https://java.net/downloads/heap-snapshot/hprof-binary-format.html

JDK  heapdump 源码

openjdk/heapDumper.cpp at 60b7a8f8661234c389e247942a0012da30146a57 · unofficial-openjdk/openjdk · GitHub

Jdk 源码

jdk8u-jdk/src/share/demo/jvmti/hprof at master · frohoff/jdk8u-jdk · GitHub

Hprof 协议

Android Hprof 协议 - 仰简的个人空间 - OSCHINA - 中文开源技术交流社区

java - hprof文件格式 - Thinbug

Class loader

Java ClassLoad详解 - i野老i - 博客园

Outofmemory

art/runtime/runtime.cc

runtime->VisitRoots(this);

-->VisitNonConcurrentRoots 

  --> VisitThreadRoots

  --> VisitNonThreadRoots

-->VisitImageRoots

-->VisitTransactionRoots

-->VisitConcurrentRoots

MAT 工具

Shallow Size (对象自身占用的内存大小)

Retained Size (被GC后Heap上释放的内存大小)

shallow heap:对象本身的大小,如果是数组或集合则是各个元素的总大小。

retained heap:对象本身的大小 + 引用的其他对象的大小。

with outgoing references(查看对象为什么消耗内存,查看对象引用的其他对象)

with incoming references(查看对象被谁引用)

-Xms200m -Xmx200m -Xmn200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\Users\80280267\IntelliJIDEAProjects\MyApplication\JavaHprof

https://www.kancloud.cn/dztec/jvm_mon/1482108

https://www.cnblogs.com/hanlinhu/p/10174185.html

计算方法

https://blog.csdn.net/u013309870/article/details/52038407

tasklist -v | find "java"    //找到java pid

netstat -nao | find  "13212"    //找到pid对应port

jstat –gcutil ${pid} 1000 

BumpPointerSpace

BumpPointSpace 是顺序分配内存,分配方法很简单,跟ZygoteSpace一样不能释放,Free()没有实现,比较实用与线程局部内存,通过clear()内存一次性释放。

art\runtime\gc\space\bump_pointer_space.cc

BumpPoninterSpace.create 会创建4K对齐memmap 内存

objects_allocated_(0), bytes_allocated_(0)   分别表示分配了多少个mirro object 对象及占用的总字节空间

其提供了两种内存分配方法:

Alloc 用于为某个mirror object 对象分配所需要的内存,对象是8字节对齐返回对象指针

AllocNewTlab 当ART 虚拟机决定从调用线程的本地存储空间中分配内存时将调用。这里也是8字节对齐,其mirro object 和占用字节空间保存在Thread.tlsPtr_中

在Heap 类中有一个bump_pointer_sapce_变量,执行BumpPointerSpace,对应空间可以被任意线程当做TLAB使用,第一个分配的线程会创建一个main block 位于内存资源头部,尾部用main_block_size_ 指定,后续的线程的TLAB都会有一个BlockHeader 来描述。

Walk 用于遍历mirror Object 对象,并回调ObjectCallback 回调

GetBytesAllocated 返回分配字节数

RegionSpace

RegionSpace内存算法是将内存资源划分为一个个固定大小(KRegionSize)1M 的内存,每个内存块由Region 对象表示,其还和Copying Collection 算法(semispace) 相关

Region_space.h

  enum class RegionType : uint8_t {

    kRegionTypeAll,              // All types.

    kRegionTypeFromSpace,        // From-space. To be evacuated.

    kRegionTypeUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.

    kRegionTypeToSpace,          // To-space.

    kRegionTypeNone,             // None.

  };

  enum class RegionState : uint8_t {

    kRegionStateFree,            // Free region.

    kRegionStateAllocated,       // Allocated region.

    kRegionStateLarge,           // Large allocated (allocation larger than the region size).

    kRegionStateLargeTail,       // Large tail (non-first regions of a large allocation).

  };

kRegionStateFree空闲内存, kRegionStateAllocated已经分配过一些内存

当分配3.5M,需要4个Region块,第一个状态是kRegionStateLarge,后面3个是kRegionStateLargeTail,并且剩下的0.5M 也不能使用。

RegionSpace.alloc 分配跟BumpPointerSpace.alloc 分配算法一样,当Copy Collector semi 算法,可用的region 空间小于总的一半时,不在分配对象;

RegionSpace.allocTLAB 跟简单,它将一个region区域用于分配,空间小于总的一半时,不在分配对象。

Walk 用于遍历所有Object 对象

RegionSpace 比BumpPointerSpace 多了Region 这一层的管理,用于返回一个Object对象所属的Region 对象,RefToRegion()

MallocSpace->

DLMallocSpace 和RosAllocSpace

DLMallocSpace使用开源dlmalloc来提供内存分配和释放算法,libc 库中malloc 函数实现

RosAllocSpace 使用谷歌开源rosalloc 内存分配管理器

Heap::CreateMallocSpaceFromMemMap 用于分配管理MallocSpace,当kUserRosAlloc 宏默认为true,优先使用RosAlloc 算法,默认初始大小是4K

Mutator 表示内存创建和修改者,一般而言,mutator 指应用程序本省(除collector部分外),与collector 是相对应的,mutator_lock 就是ART 中控制内存分配与回收线程同步

Non_moving_space 是mallocspace 一种使用场景,跟GC 有关

Ros  run of slot

kNumOfSizeBrackets  值为42,表示42 种分配内存slot 粒度,

bracketSizes[kNumOfSizeBrackets]: [0]         ->8 字节

  1.         ->16 字节
  2.         ->8 * (index + 1)
  1.        ->1k
  2.        ->2k

numOfPages[kNumOfSizeBrackets]:[0~39] 为 1,[40]为2,[41]为4 ,每种slot 可以分配的内存资源个数,以4k为单位。

numOfSlots[kNumOfSizeBrackets] 对应slot 粒度下slot 个数

Run 就是管理numOfSlots[kNumOfSizeBrackets]  个同样的slot 粒度内存资源池

headerSizes[kNumOfSizeBrackets] 是指Run 管理同样粒度slot 的头部大小,为80 字节+ bitmap + 对齐

headerSizes[kNumOfSizeBrackets] + numOfSlots[kNumOfSizeBrackets] * bracketSizes[kNumOfSizeBrackets] = numOfPages[kNumOfSizeBrackets] * 4k

RosAlloc 中有42 个Mute 锁,分别对应不同粒度内存

RosAlloc::Alloc 当对象大小大于2KB,就会调用AllocLargeObject()->AllocPage来完成,否则调用AllocFromRun(),

这里如果内存不超过128KB,则从本地线程池中分配,即tlsPtr_.rosalloc_runs数组,包含16个元素,当tls 不够会从ros 中再分配。

LargeObjectSpace有两个继承类,分别是LargeObjectMapSpace和FreeListSpace

当分配的对象(Java基础对象和String)大于large_object_threshold_(默认12KB)时,是用该对象分配,这个没有算法,当需要分配时,调用memmap 分配。

large_objects_是一个map <*obj,LargeObject> ,即对象与其对应的memmap 映射。

Heap::ChangeCollector

内存分配类型的选择是由垃圾回收器确定的

GarbageCollector.run 启动垃圾回收

art/build/art.go

gcType := ctx.Config().GetenvWithDefault("ART_DEFAULT_GC_TYPE", "CMS")

Cmdline_types.h->XGcOption

AndroidRuntime.cpp->startvm

//App 前台GC 类型

parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");

//App 后台GC类型

parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC=");

Runtime.cc->init()

前台GMS,后台HSC

前台SS,后台HSC

前台GSS,后台GSS

Heap::ChangeAllocator

->SetQuickAllocEntryPointsAllocator

quick_alloc_entrypoints.cc 这个文件中,art 虚拟机以机器码运行Java程序的时候,如果涉及到内存分配相关的指令,则需要跳转到内存分配有关的入口地址去执行。

->instrumentation.cc

ResetQuickAllocEntryPoints()

-> thread->ResetQuickAllocEntryPointsForThread();  遍历所有线程,执行对应线程reset

Class Thread {

Tls_ptr_sized_values{

QuickEntryPointsquick_entrypoints,

}

}

Tls_ptr_ 中有quick_entrypoints 成员

void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented)

对应着不同分配算法入口

String::Alloc

Array::Alloc

Class::Alloc

最终调用到 Heap::AllocObjectWithAllocator

Quick_entrypoints_x86.S

QuickEntryPoints 成员             汇编函数               C++函数

pQuickAllocObject      art_quick_alloc_object_rosalloc   artAllocObjectFromCodeRosAlloc

StringFactory.java->newEmptyString

Heap::AllocateInternalWithGc

Collector::GcType last_gc = WaitForGcToComplete  等待当前GC 完成

kGcTypeNone         没有做GC

kGcTypeSticky        仅扫描和回收上次GC到本次GC这个时间段内创建的对象

kGcTypePartial       仅扫描和回收应用进程自己的堆,不处理Zygote

kGcTypeFull          扫描app自己以及从Zygote 继承得到的堆

分配不到内存最后会抛出outofmemory

kGcTypeSticky  这种垃圾回收模式中,会将上一次GC 到本次GC 期间对象保存在Alloc Stack 中

Heap::Heap

Initial_size   art -Xms知道;android 由dalvik.vm.heapstartsize 指定,默认4MB

Heap::MemoryInitialSize

Capacity  art -Xmx ;dailvk.vm.heapsize  

Heap::MemoryMaximumSize

指定默认参数

art/runtime/runtime_options.def

ART_BASE_ADDRESS  指定art heap 基地址 0x70000000(256M)

ART_BASE_ADDRESS_MIN_DELTA =-16M

ART_BASE_ADDRESS_MAX_DELTA =16M

基地址随机生成一个偏移

12c00000-52c00000(300M-1324M)

Initial   ---4M

Heap   ---512M

fg-gc  CollectorTypeCC;bg-gc CollectorTypeCCBackground

growth_limit  ---256M

non_moving_space_capacity   ---64M

large_object_threshold    ---12k

use_homogeneous_space_compaction_for_oom 1;use_generational_cc 1

separate_non_moving_space = true

heap_reservation_size = non_moving_space_capacity

dalvik-zygote space

Base_addr 70000000

image_reservation_size  ----55M

extra_reservation_size   ----64M

创建匿名memmap(addr + random,(55+64)M)

boot_images_start_address_ = Base_addr + ramdom

boot_images_size_ = 55M

匿名映射119M,分配ImageSpace 映射55M,接着remapatend 创建名为 zygote space 的non moving sapce,是用DlMallocSpace 在 non moving space 中分配名为zygote / non moving space ,初始大小4M

Heap 4 个关键类--CradTable、RememberedSet、ModUnionTable、ObjectStack

在分代GC 中,当新生代的对象被老年代引用,这个时候回收新生代不扫描老年代就会误回收新生代对象;RememberedSet 就是记录这些引用了新生代的老年代对象;

CardTable 是用一个字节(Dirty card)表示连续128字节的space 中是否有引用新生代对象;

在ART 中,RememberedSet 以Card 为单位管理跨Space的Object

ModUnionTable 跟RememberedSet 差不多,前者用于ImageSpace和ZygoteSpace,后者用于RosAllocSpace和DlMallocSpace

WriteBarrier 当给一个对象赋引用型值,这会调用WriteBarrier,将对象对应card 设置为Dirty,表明该对象被引用了,这个时候会调用MarkCard设置为dirty

Object VisitReferences

用户访问一个Object对象的引用型成员变量

Mirror class 有两个成员变量和标志位

Access_flag_

Class_flags   描述Object 所属类的类型   class_flags.h

art/runtime/mirror/Object.h

  template <bool kVisitNativeRoots = true,

            VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,

            ReadBarrierOption kReadBarrierOption = kWithReadBarrier,

            typename Visitor,

            typename JavaLangRefVisitor = VoidFunctor>

  void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)

      NO_THREAD_SAFETY_ANALYSIS;

Visitor 和JavaLangRefVisitor 用于通知调用者

Gc root

art/runtime/gc_root.h

RootType {

  kRootUnknown = 0,

  kRootJNIGlobal,

  kRootJNILocal,

  kRootJavaFrame,

  kRootNativeStack,

  kRootStickyClass,

  kRootThreadBlock,

  kRootMonitorUsed,

  kRootThreadObject,

  kRootInternedString,

  kRootFinalizing,  // used for HPROF's conversion to HprofHeapTag

  kRootDebugger,  

  kRootReferenceCleanup,  // used for HPROF's conversion to HprofHeapTag

  kRootVMInternal,

  kRootJNIMonitor,

};

kRootStickyClass    -------从ImageSpace 获取的对象

kRootJavaFrame    -------函数调用时引用参数

kRootVMInternal   --------runtime 成员变量 sentinel_、pre_allocated_OutOfMemoryError_等

runtime->VisitRoots(this);

-->VisitNonConcurrentRoots 

  --> VisitThreadRoots

  --> VisitNonThreadRoots

-->VisitImageRoots

-->VisitTransactionRoots

-->VisitConcurrentRoots

关键数据结构

Class RootInfo

描述root 信息,包括类型及它所在的线程id,不是所有的都跟线程相关,默认id 为0;

Class GcRoot 模板类

一个GcRoot 的实例代表一个被认为跟的root 对象,其成员变量 root_是一个地址,代表Object 对象

GcRootSource

两个域:ArtField* field_

        ArtMethod* method_

class RootVisitor {

 public:

  virtual ~RootVisitor() { }

  // Single root version, not overridable.   访问单个root object 对象

  ALWAYS_INLINE void VisitRoot(mirror::Object** root, const RootInfo& info)

      REQUIRES_SHARED(Locks::mutator_lock_) {

    VisitRoots(&root, 1, info);

  }

  // Single root version, not overridable.

  ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** root, const RootInfo& info)

      REQUIRES_SHARED(Locks::mutator_lock_) {

    if (*root != nullptr) {

      VisitRoot(root, info);

    }

  }

//两个函数访问一组root  object 对象

  virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)

      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,

                          const RootInfo& info)

      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

};

BufferedRootVisitor  是用于收集root object 并一次性访问它们

Java Reference

Reference<T> 是一个模板抽象类,有四个派生类,分别是SoftReference、WeakReference、PhantomReference和FinalizeReference,其中FinalizeReference 是一个隐藏类,供SDK 内部使用,当对象的类有finalize 函数会调用;ReferenceQueue<T>用于管理多个Reference 对象;PhantomReference必须与ReferenceQueue配合使用。

假设一个非Reference 对象obj ,对Gc 而言有三种可能

  1. 通过非Reference的引用可以搜索到obj ,不会回收
  2. 通过Reference 的引用搜索到obj ,这种情况取决于引用它的那个Reference 对象数据类型及GC策略
  3. 无法搜索到,会被回收

在CMS中,如果应用obj 的refobj 为:

Refobj 类型为SoftReference 类型,则GC 不一定释放,在CMS中StickyMS不会释放;在Partial时和FullMS均会回收

在refobj 是WeakReference ,每次GC 都会被释放

Refobj 是PhantomReference 时,跟没有引用一样,这个主要是让使用者知道obj 被回收了,PhantomReference和Finalize都是一个让用户知道哪个对象被回收了,相比Finalize 有一定性能损耗,

对于一个 Java 程序而言,对象都位于堆内存块中,存活的那些对象都被根节点引用着,即根节点 GC Roots 是一些引用类型,自然不在堆里,那它们位于哪呢?它们能放在哪呢?

答案是放在栈里,包括:

Local variables 本地变量

Static variables 静态变量

JNI References JNI引用等

如何快速枚举 GC Roots?

1、笨方法:遍历栈里所有的变量,逐一进行类型判断,如果是 Reference 类型,则属于 GC Roots。

2、高效方法:从外部记录下栈里那些 Reference 类型变量的类型信息,存成一个映射表 -- 这就是 OopMap 的由来 。

“在解释执行时/JIT时,记录下栈上某个数据对应的数据类型,比如地址1上的”12344“值是一个堆上地址引用,数据类型为com.aaaa.aaa.AAA)

现在三种主流的高性能JVM实现,HotSpot、JRockit和J9都是这样做的。其中,HotSpot把这样的数据结构叫做 OopMap ,JRockit里叫做livemap,J9里叫做GC map。”

GC 时,直接根据这个 OopMap 就可以快速实现根节点枚举了。

深入理解class 文件

ClassFile {

U4 magic;    //0xcafebabe固定

U2 minor_version;

U2 major_version;

U2 constant_pool_count; //常量池中元素个数

Cp_info constant_pool[constant_pool_count-1];  //索引从1开始

U2 access_flag;

U2 this_class;  //类名在常量数组池中索引

U2 super_class;  //类名在常量数组池中索引

U2 interfaces_count; //实现接口数

U2 interfaces[interfaces_count];   //实现接口类对应的常量池索引

U2 fields_count;

Field_info fileds[fileds_count];

U2 methods_count;

Method_info methods[methods_count];

U2 attributes_count;

Attribute_info [attributes_count];

}

ART/app SIGSEGV 信号处理流程

ART虚拟机 | Android应用中SIGSEGV信号的处理流程 - 掘金

Runtime::Init(...)

{

....UnHandleException .....

BlockSignals();

InitPlatformSignalHandlers();

...

//dex2oat 是没有使能signalchin

  if (!no_sig_chain_) {

    // Dex2Oat's Runtime does not need the signal chain or the fault handler.

    if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {

      fault_manager.Init();

      // These need to be in a specific order.  The null point check handler must be

      // after the suspend check and stack overflow check handlers.

      //

      // Note: the instances attach themselves to the fault manager and are handled by it. The

      //       manager will delete the instance on Shutdown().

      if (implicit_suspend_checks_) {

        new SuspensionHandler(&fault_manager);

      }

//堆栈溢出检测

      if (implicit_so_checks_) {

        new StackOverflowHandler(&fault_manager);

      }

//空指针检测

      if (implicit_null_checks_) {

        new NullPointerHandler(&fault_manager);

      }

      if (kEnableJavaStackTraceHandler) {

        new JavaStackTraceHandler(&fault_manager);

      }

    }

  }

}

void FaultManager::Init() {

  CHECK(!initialized_);

  sigset_t mask;

  sigfillset(&mask);

  sigdelset(&mask, SIGABRT);

  sigdelset(&mask, SIGBUS);

  sigdelset(&mask, SIGFPE);

  sigdelset(&mask, SIGILL);

  sigdelset(&mask, SIGSEGV);

  SigchainAction sa = {

    .sc_sigaction = art_fault_handler,

    .sc_mask = mask,

    .sc_flags = 0UL,

  };

...........

这里调用 InitializeSignalChain ,将linked_sigaction/64、linked_sigprocmask/64 替换为sigaction/sigprocmask;接着调用

chains[signal].AddSpecialHandler(sa);将SIGSEGV 对应sa 加入到special_handlers

chains[signal].Claim(signal);// 调用Register(signo),将SIGSEGV原有的注册函数debuggerd_signal_handledr指针存入action_字段,将SignalChain::Handler注册为新的处理函数。

  AddSpecialSignalHandlerFn(SIGSEGV, &sa);

  initialized_ = true;

}

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值