我是靠谱客的博主 呆萌向日葵,最近开发中收集的这篇文章主要介绍openjdk 源码分析 v0.1第一节 参考 第二节.openjdk源码模块 第三节.常用类 第四节.垃圾回收算法第五节.多线程,同步与可见性,内存模型(重排序)第六节.IO/NIO/AIO,reactor/proactor第七节 对象结构,字节码与模板解释器第八节 监控工具,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

第一节 参考

 

一.openjdk7:

https://blog.csdn.net/hcj116/article/details/54946551

https://blog.csdn.net/j754379117/article/details/53695426

https://www.jianshu.com/p/e53e7964db03

http://www.txazo.com/jvm/openjdk-compile.html

https://super2bai.github.io/JVM/build.html

https://www.jianshu.com/p/5107fc72558f

http://www.cnblogs.com/zyx1314/p/5638596.html

https://stackoverflow.com/questions/6000554/clang-complete-where-is-the-libclang-so-dylib-in-os-xhttps://liuzhengyang.github.io/2017/04/28/buildopenjdk/

sudo ln -s /Users/feivirus/Documents/software/apache-ant-1.8.2/bin/ant  /usr/bin

sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib /usr/local/lib/

二.openjdk8:

https://blog.csdn.net/manageer/article/details/72812149

三.openjdk9:

https://juejin.im/post/5a6d7d106fb9a01ca47abd8b

https://segmentfault.com/a/1190000005082098

https://www.gonwan.com/2017/12/01/building-debugging-openjdk8-from-source-on-macos-high-sierra/

https://www.zhihu.com/question/52169710

process handle SIGSEGV --stop=false

https://medium.com/@maxraskin/background-1b4b6a9c65be

add-dsym /Users/feivirus/Documents/project/eclipse/jdk9/build/macosx-x86_64-normal-server-slowdebug/support/modules_libs/java.base/server/libjvm.dylib.dSYM

https://apple.stackexchange.com/questions/309017/unknown-error-2-147-414-007-on-creating-certificate-with-certificate-assist

 

第二节.openjdk源码模块

 主要内容: 类二分模型,类加载,堆栈结构,解释器的模板表与转发表,编译器,函数分发指令集.

一.hotspot源码结构

(一)vm根目录下:
 1.Adlc:平台描述文件(cpu或os_cpu目录中的.ad文件)的编译器
 2.asm:汇编器
 3.c1 Client编译器
 4.ci 动态编译器的公共服务(从动态编译器到VM的接口)
 5.classfile 处理类文件(包括类加载和系统符号表等)
 6.code 管理动态生成的代码
 7.compiler 从VM调用动态编译器的接口
 8.gc_implementation GC实现
 9.gc_interface GC接口
 10.interpreter 解释器,包括模板解释器(官方版使用)和C++解释器(官方版未用)
 11.libadt:抽象数据结构
 12.memory 内存管理相关实现(老的分代式 GC 框架也位于此处)
 13.oops HotSpot VM的对象系统的实现
 14.opto Server编译器(即C2)
 15.prims HotSpot VM的对外接口,包括部分标准库的native部分实现和JVMTI实现
 16.rumtime 运行时支持库(包括线程管理、编译器调度、锁、反射等)
 17.services 用于支持JMX之类的管理功能的接口
 18.shark 基于LLVM的JIT编译器(官方版未用)
 19.utilities 一些基本工具类

(二).prims对外接口模块

主要包括jni,perf,jvm,jvmti四个模块.

1.jni模块(java native interface,允许java代码与本地代码交互,以 jni_前缀)

jni知识参考 https://blog.csdn.net/column/details/blogjnindk.html

2.jvm模块(对jni的补充,以jvm_前缀)

涉及jvm.h文件等.主要包含导出函数,比如访问jvm状态,字节码和class文件格式校验,各种IO和网络操作.

3.jvmti模块

监控和调优java程序

4.perf模块

以perf_为前缀,监控虚拟机.

(三).services模块

通过jmx监控和管理java应用。jmx参考https://www.cnblogs.com/dongguacai/p/5900507.html

分为Management,MemoryService,ThreadService,RuntimeService,MemoryManager,HeapDumper,ClassLoadingService,MemoryPool,AttachListener九个子模块.

(四)Runtime模块

分为Thread(线程队列),Arguments(vm参数解析),StubRoutines/StubCodeGenerator(Stub例程),Frame栈帧(frame.hpp),CompilationPolicy(编译策略),Init(系统初始化),VmThread模块(全局单例线程,维护操作队列VmOperationQueue,执行GC,对外提供监控), VMOperation(比如ThreadStop,FindDeadlocks,ForceSafepoint,ParallelGCSystemGC等),互斥锁,安全点,PerfData,,反射,

二.虚拟机启动


(一)虚拟机的生命周期
启动器分为通用启动器(Generic Launcher)和调试版启动器(gamma)两种.
通用启动器就是java和javaw,区别是javaw没有控制台窗口.gamma启动器入口位于hotspot/src/share/tools/luncher/java.c中.通用启动器入口在jdk/src/share/bin/main.c中(jdk9改在jdk/src/java.base/share/native/launcher下).
jdk/src/share/bin/main.c的main()->jdk/src/share/bin/java.c的JLI_Launch()->jdk/src/solaris/bin/java_md_solinux.c的JVMInit()新建线程调用JavaMain()->jdk/src/share/bin/java.c的JavaMain()(jvm启动核心操作)在该方法中依次调用InitializeJVM()初始化jvm(此方法进入hotspot/src/share/vm/prims/jni.cpp的JNI_CreateJavaVM()中->hotspot/src/share/vm/runtime/thread.cpp中的Threads::create_vm()->hotspot/src/share/vm/runtime/vmThread.cpp的VMThread::create()),调用LoadMainClass()加载主类,调用GetStaticMethodID()获取main方法的id号,调用CreateApplicationArgs()创建java的main方法的参数,调用CallStaticVoidMethod()进入java的main方法->javac中的DetachCurrentThread()->javac中的DestroyJavaVM()

启动过程的堆栈如下图

jvm启动

进入java代码的main()方法前的堆栈如下图:

jvm调用

其中StubRoutines::call_stub()是个函数指针,指向被调用的java的main函数地址.

(二)过程分析
1.main.c文件的main方法,主要是获取java进程参数,调用LoadJavaVM()加载libjvm,启动新线程启动JavaMain()方法.


第三节.常用类

主要是集合相关类,比如
Hashmap的红黑树,synchronized,proxy,serviceloader,threadpoolexecutor,classloader,aqs等.


第四节.垃圾回收算法


一.看垃圾回收的日志
2019-12-19T11:26:16.942-0800: 0.493: [GC (Allocation Failure) [PSYoungGen: 33264K->5110K(38400K)] 33264K->5950K(125952K), 0.0045817 secs] [Times: user=0.01 sys=0.01, real=0.00 secs] 

2019-12-19T11:26:16.942:gc开始时间
-0800:中国所在的东8区
0.493:相对于JVM启动时的间隔时间,单位是秒
GC:代表Minor GC。此字段还可以为Full GC.
Allocation Failure:触发gc的原因.此字段还可以是Ergonomics,代表jvm的自动gc.
PSYoungGen:年轻代还是年老代.
33264K->5110K:gc之前和gc之后,年轻代的使用量
38400K:年轻代总的空间大小
33264K->5950K:gc之前和gc之后,整个堆内存的使用情况
125952K:可用的堆的总的空间大小
0.0045817 secs:gc持续时间大小
user,sys,real:分别是gc消耗时间和,系统调用时间,stw的时间.

二.不同垃圾回收算法的核心区别
1.gc的过程是标记->清除->整理.
2.垃圾收集器的种类
Serial/Serial Old:串行GC.最古老的,单线程收集器.年轻代使用mark-copy,年老代使用mark-sweep-compact(标记-清除-整理)算法.两者都会触发STW.设置-XX:+UseSerialGC.
ParNew:Serial的多线程版本
Parallel Scavenge:年轻代的多线程收集器,默认收集器,采用mark-copy算法,回收期间不stw.
Parallel Old:Parallel Scavenge收集器的年老版本,采用Mark-sweep-Compact算法.
CMS:采用Mark-Sweep算法,最短的stw时间.年轻代使用Parallel New,mark-copy算法.年老代使用CMS,mark-sweep算法.在Initial Mark初始标记和Final REMARK重新标记时会stw.缺点是老年代内存碎片问题,在某些情况下GC会造成不可预测的暂停时间,特别是堆内存较大的情况下.依次经历Initial Mark->Concurrent Mark->Concurrent Preclean->Concurrent Abortable Preclean->Final Remark->Concurrent Sweep->Concurrent Reset过程.
G1:预测STW时间.
3.标记之后可以有清除,整理,复制三种处理方式.
清除的缺点是还有很多空闲内存, 却可能没有一个区域的大小能够存放需要分配的对象, 从而导致分配失败(在Java中就是OutOfMemoryError).
整理的缺点就是GC暂停时间会增加, 因为需要将所有对象复制到另一个地方, 然后修改指向这些对象的引用。
复制的缺点则是需要一个额外的内存区间, 来存放所有的存活对象。
4.年轻代和老年代处理方式不同。年轻代的特点是时间短,小对象。年老代的特点是默认都是存活的,时间长,大对象.
默认的gc设置是-XX:+UseParallelGC.在年轻代使用Parallel Scavenge,年老代使用Parallel Old.

 

第五节.多线程,同步与可见性,内存模型(重排序)


一.线程同步
内存一致性:对同一个变量,在物理内存中存一个,在不同的线程中,各有一份自己的缓存,需要保持这个变量的读写相等.
主要包括原子性,有序性,可见性.jvm自己的多线程的可见性.
缓存一致性协议:cpu的L1,L2,每个cpu有自己的写缓冲区,interl的mesi.主要是cpu硬件可见性.
二.重排序
编译器重排序
处理器重排序:指令重排序,内存系统重排序
内存屏障指令禁止处理器重排序:LoadLoad,StoreStore,LoadStore,StoreLoad
三.happens-befores规则
四.volatile
1.源码在jdk的LIRGenerator::volatile_field_load()方法中处理.
2.读写前后加内存屏障指令
3.写volatile变量,线程本地变量会刷新到主内存.读volatile的变量,线程本地变量会置无效,从主内存读取值.
五.final 也会加屏障,防止初始化时,读到前后两个值.
六.synchronized
1.分类:
锁对象(普通方法,加ACC_SYNCHRONIZED修饰符),锁class(静态方法),锁代码块(通过monitorenter,monitorexit指令实现).
2.实现方式:
(1).java对象头的Mark Word有轻量级锁,重量级锁和偏向锁的标志位.
(2).Monitor机制.每个线程有个monitor列表.
3.锁
(1)种类:自旋锁(默认自旋10次),适应性自旋锁(根据上次自旋成功或失败判断),锁消除,锁粗化,偏向锁,轻量级锁.
(2)状态:无锁,偏向锁,轻量级,重量级,锁只能升级不能降级.
七.AQS
1.使用CLH同步队列.lock时,入队列,当前节点变为尾节点,自旋判断prev节点是否释放锁.unlock出队时,把当前节点的锁状态释放。这样,后继节点就能拿到锁.
2.分为独占式和共享式.
3.使用LockSupport,通过Unsafe阻塞或者唤醒线程.
八.ReentrantLock
1.排他锁.继承AQS,分为公平锁,非公平锁,FairSync和NonFairSync.
九.ReentrantReadWriteLock
1.读写锁.可重入,支持公平性和非公平性.锁降级(写锁降级为读锁,获取写,获取读,释放写).
2.读锁内部维护HoldCounter计数器。
十.condition
1.包含一个FIFO队列,每个节点包含一个线程引用.调用await方法时加入队列尾部.signal方法唤醒队列的第一个节点线程.
十一.CAS
1.实现方式:通过处理器的LOCK#信号做总线加锁.缓存加锁.
2.问题:循环时间长.只能保证一个共享变量原子操作.ABA问题(加版本号).
十二.CyclicBarrier
1.内部使用ReentrantLock和Condition.
十三.CountDownLatch
1.内部依赖Sync实现,Sync继承自AQS.
2.await()在计数器到0之前一直等待.countDown()线程递减计数器.
十四.Semaphore
可用数量,获取到一个Semaphore,可用数量减1.Semaphore为0时,线程等待.
十五.Exchanger
Exchanger对象理解为一个包含两个格子的容器,通过exchanger方法可以向两个格子中填充信息。
十六.ConcurrentHashMap

 

第六节.IO/NIO/AIO,reactor/proactor

 

第七节 对象结构,字节码与模板解释器


一.对象结构
(一)Object结构
在jdk1.8,mac 64位下,空对象占用16个字节
Object = Header + Primitive Fields + Reference Fields + Alignment & Padding
1.对象头Header
Header: mark word + Klass pointer
mark word:64位是8字节,32位是4字节.
Klass pointer:32位是4字节,64位默认开启UseCompressedOops是4字节,不开启8字节.
空对象64位下是8+4=12 字节.
2.成员属性Primitive Fields + Reference Fields
空对象没有这部分.不同数据类型大小不同,比如int 4 bytes,double 8 bytes,引用一个word大小.数组也是对象,header中有一个int类型的length值,多4个字节.
3.对齐Alignment和补齐Padding
对齐:任何对象都是 8字节对齐.比如空对象,12 bytes的header部分,对齐加4个字节.
补齐:字节补齐4个字节.
(二)对象重排序
在heap中对field成员属性进行重排序,节省空间.
分配顺序原则:1.double > long > int > float > char > short > byte > boolean > object reference
2.Padding Size(4 bytes)的类型的优先级就高于大小>Padding Size的类型
3.子类和父类的field不混在一起,父类的field分配完再分配子类.
4.父类的的最后一个字段与子类的第一个字段以一个Padding Size(4 bytes)对齐

(三).Oop/Klass 模型
当在java中new一个对象时,在c++层面是在堆内存创建一个instanceOopDesc对象.instanceOopDesc对象通过元数据指针_metadata指向InstanceKlass元数据,比如方法区信息.
1.oop:Ordinary Object Pointer,表示对象的实例信息.class instanceOopDesc描述java层面的对象,就是对象头.继承自oopDesc.
class oopDesc {
  ...
 private:
  //对象头信息,markOop是个markOopDesc*类指针?但实际是个Word,8字节,一个字长,存储具体的数据
  //markOopDesc在markOop.hpp中定义.
  //成员变量有hashcode,gc分代年龄,锁状态标志等.
  //在markOop.hpp中文件注释有说明各种标志位。通过各种位操作函数修改这里面比特位的值.
  volatile markOop  _mark;
  //联合,只存一种
  union _metadata {
    //InstanceKlass 对象的指针
    Klass*      _klass;
    //压缩指针
    narrowKlass _compressed_klass;
  } _metadata;
  ...
}
markOop部分文件注释如下:
//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

2.Klass:存储对应c++对象的虚表等类的元数据信息.在元空间方法区.
InstanceKlass类,继承自Klass类,在instanceKlass.cpp的最上方文件注册中画出了InstanceKlass布局,包含
//  InstanceKlass layout:
//    [C++ vtbl pointer           ] Klass
//    [subtype cache              ] Klass
//    [instance size              ] Klass
//    [java mirror                ] Klass
//    [super                      ] Klass
//    [access_flags               ] Klass
//    [name                       ] Klass
//    [first subklass             ] Klass
//    [next sibling               ] Klass
//    [array klasses              ]
//    [methods                    ]
//    [local interfaces           ]
//    [transitive interfaces      ]
//    [fields                     ]
//    [constants                  ]
//    [class loader               ]
等.每个字段对应一个InstanceKlass的成员属性.ClassFileParser把class文件在运行时解析成InstanceKlass对象.Klass类中有对应java层面Setter/Getter方法的转换函数.


二.字节码

三.模板解释器
核心三个文件模板解释器templateInterpreter.cpp,模板解释器生成器templateInterpreterGenerator,模板表templateTable.cpp.
解释器分为两种,汇编类型TemplateInterpreterGenerator和c++类型CppInterpreterGenerator,共同父类是AbstractInterpreter.
1.解释器结构
每个模板的定义在templateTable.hpp中的Template类中,属性包含标志位,栈状态,目标代码等.
2.初始化过程
(1).字节码处理函数定义
在jvm启动时调用TemplateTable::initialize(),对每一个字节码调用TemplateTable::def(),定义处理函数.def的generator列是对应的处理函数,不过这个是个宏定义.TosState是字节码执行前后的栈顶状态.宏定义在templateTable.cpp中.比如#define istore TemplateTable::istore.
比如如果查找istore指令的处理方法,直接在TemplateTable.cpp或者对应的cpu架构下TemplateTable_xxx.cpp中搜索 TemplateTable::istore就可以搜到对应处理函数.在templateTable_x86_64.cpp中代码如下:
void TemplateTable::istore() {
  transition(itos, vtos);
  locals_index(rbx);
  //存到局部变量表
  __ movl(iaddress(rbx), rax);
}
(2).字节码处理函数使用
在Bytecodes::Code枚举中定义了所有的可以用的字节码.在TemplateInterpreterGenerator::set_entry_points_for_all_bytes()方法中遍历Bytecodes::Code枚举,对每个字节码枚举调用TemplateInterpreterGenerator::set_entry_points()方法设置处理函数.这个方法的实现在templateInterpreter.cpp文件中.set_entry_points()方法针对每个字节码生成两种处理函数,分别调用TemplateTable::template_for()和TemplateTable::template_for_wide(),窄版和宽版.在这两个方法里面进入TemplateInterpreterGenerator::set_short_entry_points()中.调用会调用TemplateInterpreterGenerator::generate_and_dispatch().在这个方法中有个类似aop的操作。

void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
  if (PrintBytecodeHistogram)                                    histogram_bytecode(t);
#ifndef PRODUCT
  // debugging code
  if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) count_bytecode();
  if (PrintBytecodePairHistogram)                                histogram_bytecode_pair(t);
  if (TraceBytecodes)                                            trace_bytecode(t);
  if (StopInterpreterAt > 0)                                     stop_interpreter_at();
  __ verify_FPU(1, t->tos_in());
#endif // !PRODUCT
  int step = 0;

  if (!t->does_dispatch()) {
    step = t->is_wide() ? Bytecodes::wide_length_for(t->bytecode()) : Bytecodes::length_for(t->bytecode());
    if (tos_out == ilgl) tos_out = t->tos_out();
    // compute bytecode size
    assert(step > 0, "just checkin'");
    // setup stuff for dispatching next bytecode
    if (ProfileInterpreter && VerifyDataPointer
        && MethodData::bytecode_has_profile(t->bytecode())) {
      __ verify_method_data_pointer();
    }
    //执行前的pro处理
    __ dispatch_prolog(tos_out, step);
  }
  // generate template
  //产生模板代码
  t->generate(_masm);
  // advance
  //执行字节码
  if (t->does_dispatch()) {
#ifdef ASSERT
    // make sure execution doesn't go beyond this point if code is broken
    __ should_not_reach_here();
#endif // ASSERT
  } else {
    // dispatch to next bytecode
    //调度下一个字节码
    __ dispatch_epilog(tos_out, step);
  }
}

第八节 监控工具

一.工具

hsdb,aprof,gcviewer,hprof,hsdis,jconsole,jhat,jinfo,jmap,jstack,visualvm

最后

以上就是呆萌向日葵为你收集整理的openjdk 源码分析 v0.1第一节 参考 第二节.openjdk源码模块 第三节.常用类 第四节.垃圾回收算法第五节.多线程,同步与可见性,内存模型(重排序)第六节.IO/NIO/AIO,reactor/proactor第七节 对象结构,字节码与模板解释器第八节 监控工具的全部内容,希望文章能够帮你解决openjdk 源码分析 v0.1第一节 参考 第二节.openjdk源码模块 第三节.常用类 第四节.垃圾回收算法第五节.多线程,同步与可见性,内存模型(重排序)第六节.IO/NIO/AIO,reactor/proactor第七节 对象结构,字节码与模板解释器第八节 监控工具所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部