阅读JVM Internals的笔记
JVM System Threads
用户运行的main函数并不是java中最初始的线程,java开始运行的时候就对应着机器中的一个线程,可以称之为Java Thread,当java中其他的线程(non-deamon)的都结束以后,Java Thread才会终止,可以认为是java本身一个代表
在main运行之外,还有一些java system thread来在幕后支撑main函数运行,比如:
- VM thread 等到JVM到达safe point,例如“stop-the-world”垃圾回收,线程的栈dump,线程暂停和biased locking revocation(有偏向的锁的撤回?)
- Periodc task thread 阶段性任务;一些计时事件
- GC Thread 垃圾回收
- Compiler thread 在运行时将字节码转成原本的代码的线程
- Signal dispatcher thread处理发送给JVM线程的信号并调用对应的JVM方法
对于每一个thread来说,有一些必要的组件:
- PC 程序计数器 其中native线程的PC是不确定的
- Stack 栈 每一个被调用的方法都有一个对应的frame被放入栈中,遵循LIFO原则 其中栈空间除了push和pop是不被直接管理支配的,并且frame中的一些结构可能被分配到堆上,因此这些object所占用的空间未必连续
- Native Stack 为native方法准备的 和C stack一样 因为Java Native Invocation用C语言的连接模型(linkage model)实现
- Stack Restrictions 大小可以固定或者灵活,超出范围是栈溢出,没有多余空间可分配是OutOfMemoryError
- Frame 对每一次方法调用都会配备的相关信息,在抛出异常或者方法返回的时候被pop出栈
- Local Variable Array 一些基本数据类型,对象只保存引用,返回地址,this指针
- OperandStack 作用类似于CPU中的general-purpose reg(通用寄存器)因为对变量操纵频繁 因此local variable array和operand stack数据交换也很频繁(具体看ClassFile里字节码的写法)
- Dynamic Linking 与c在编译中确定方法汇编和所在文件位置不同 在java当中方法编译后的链接是在运行时动态完成的 在类被确定并加载,调用静态方法或者动态方法的时候,symbolic reference从一个逻辑上的指针经由lazy resolution被解析,被替换成直接的引用,如果直接引用中涉及到的类没有被解析,那么就会去加载这个类。每个直接引用以相对于运行时变量或者方法存储的相对位置(offset)被储存
Shared Between Thread
Heap
Object和Array数据结构都是存放在堆中的,因为frame并不是为空间大小会变化的数据结构设计的。并且不像简单的数据类型和引用,堆中的对象并不会随着方法返回而被销毁。
在堆中存在垃圾管理机制,被分为三种类型:young generation(常被分为Eden&Survivor),old generation,permanent generation
内存管理
新对象被创建时进入young generation;minor garbage是把young generation中的对象从eden移到survivor,此时对象仍然活跃;
major garbage是把活跃的对象从young generation移动到old generation,通常会使得线程暂停;
permanent generation在old generation回收的时候回收,当其中有一个满的时候两者都会回收
Non-Heap Memory
一些不在Heap中的结构
- permanent generation 含有方法域和interned string的
- code cache 已经被JIT compiler编译的部分方法的代码