《深入理解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 这个类,其中几个重要方法是
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:
runtime_options.def 定义了art 虚拟机需要的参数信息
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内存分配跟踪
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 TrackingAllocator continuous_space_bitmaps_; // Sets covering discontinuous spaces. std::vector TrackingAllocator large_object_bitmaps_; art的bitmap使用_jamousjang的博客-CSDN博客 ART中创建的Space类型由ART 启动参数决定的,Heap 对象会记录所有创建的Space。 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 ObjectArray 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详解 --- 垃圾回收机制 - 简书 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粒度。 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 - 简书 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 各个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 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 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内存算法是将内存资源划分为一个个固定大小(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() 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 字节 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 映射。 内存分配类型的选择是由垃圾回收器确定的 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 存储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 ObjectArray 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 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详解 --- 垃圾回收机制 - 简书 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粒度。 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 - 简书 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 各个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 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 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内存算法是将内存资源划分为一个个固定大小(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() 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 字节 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 映射。 内存分配类型的选择是由垃圾回收器确定的 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对象的引用型成员变量 Mirror class 有两个成员变量和标志位 Access_flag_ Class_flags 描述Object 所属类的类型 class_flags.h art/runtime/mirror/Object.h template 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 用于通知调用者 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 const RootInfo& info) REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; BufferedRootVisitor 是用于收集root object 并一次性访问它们 Reference 假设一个非Reference 对象obj ,对Gc 而言有三种可能 在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 就可以快速实现根节点枚举了。 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虚拟机 | 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; } ImageSpace
JVM详解 --- 垃圾回收机制
Native内存泄漏
Heap dump 接口
class_linker.cc
MAT 工具
BumpPointerSpace
RegionSpace
MallocSpace->
DLMallocSpace 和RosAllocSpace
Heap::ChangeCollector
ImageSpace
ART运行时CompactingGC为新创建对象分配内存的过程分析 - 爱问共享资料JVM详解 --- 垃圾回收机制
Native内存泄漏
Heap dump 接口
class_linker.cc
MAT 工具
BumpPointerSpace
RegionSpace
MallocSpace->
DLMallocSpace 和RosAllocSpace
Heap::ChangeCollector
Object VisitReferences
Gc root
Java Reference
深入理解class 文件
ART/app SIGSEGV 信号处理流程