计算机存储元件:寄存器和内存
内存包括:内核空间和用户空间。所谓内核空间就是操作系统运行需要占用的,而用户空间则是应用程序需要占用的。
字长:CPU一次能并行处理二进制的位数(Bit)
Java内存区域:堆是用来存放对象而栈是用来执行程序的。
运行时数据区域:绿色为所有线程共享区域,白色为线程独有区域。
1. 线程独有的内存区域
(1)PROGRAM COUNTER REGISTER, 程序计数器
区域小,当前线程所执行的字节码的行号指示器,通过计数器值的改变来选取下一条需要执行的字节码指令,若执行的是native方法,则这个计数器就是空的。
(2)JAVA STACK,虚拟机栈,每个方法执行的同时都会创建一个栈祯,用于存储局部变量表,操作数栈等信息
(3)NATIVE METHOD STACK,方法栈
2. 线程间共享的内存区域
(1)HEAP,堆,存放对象的实例,有新生代和老年代之分
(2)METHOD AREA,方法区,存放常量和静态变量
(3)RUNTIME CONSTANT POOL,运行时的常量池,方法区的一部分
3. 直接内存,并不是Java虚拟机中定义的部分
4. 垃圾回收算法
(1)标记-清除(Mark-Sweep)算法
(2)复制(Coping)算法 1块Eden 和 两块Survivor,每次回收Eden和一块Survivor,将其中还活着的对象复制到另外一块Survivor,当Survivor放不下时,需要老年代进行担保
(3)标记-整理(Mark-Compact)算法
5. 垃圾收集器
(1)Serial收集器:采用复制算法的单线程收集器,Client模式下的默认新生代收集器
(2)ParNew收集器:多线程版的Serial收集器,Server模式下默认的新生代收集器
(3)Parallel收集器:复制算法并行的所线程收集器,关注的是吞吐量(CPU用于运行用户代码时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),Server模式下的默认垃圾收集器。 停顿时间短适合需要与用户交互的程序,良好的响应速度能提升用户体验;高吞吐量则可以高效率利用CPU时间,尽快完成运算速度,主要适合在后台运算而不需要太多交互的任务。
(4)Serial Old收集器:Serial收集器的老年代版本,使用 标记-整理 算法
(5) Parallel Old收集器:Parallel收集器的老年代版本,使用多线程和标记-整理算法,吞吐量优先收集器
(6)CMS(Concurrent Mark Sweep)收集器:获取较短回收停顿时间为目标的老年代收集器,使用标记-清除算法
(7)GI(Garbage-First)JDK1.7后引入,将堆的内存区域分为多个大小相等的独立区域(Region),虽然还保留了老年代和新生代的概念,但不再是物理隔离的。后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,在有限的时间内可以获取尽可能高的收集效率。
6. GC日志
(1)GC Full GC 表示垃圾回收的停顿类型,调用了System.GC 才会触发Full GC
(2) GC中接下来的 “DefNew”、“ParNew”、“PSYoungGen”、“CMS"表示老年代垃圾回收器的名称
(3) 方法内部的 ”320K->194K(2368K)",指的是该区域已使用的容量->GC后该内存已使用的容量。方括号外面的“310K->194K(7680K)”,指GC前Java堆已使用的容量->GC后Java堆已使用的容量(Java堆总容量)
(4) 再往后“0.0269163 secs”表示该内存区域GC所占用的时间,单位是秒。最后的“[Times: user=0.00 sys=0.00 real=0.03 secs]”则更具体了,user表示用户态消耗的CPU时间、内核态消耗的CPU时间、操作从开始到结束经过的钟墙时间。后面两个的区别是,钟墙时间包括各种非运算的等待消耗,比如等待磁盘I/O、等待线程阻塞,而CPU时间不包括这些耗时,但当系统有多CPU或者多核的话,多线程操作会叠加这些CPU时间所以如果user或sys超过real是完全正常的。
(5) “Heap” 后面就列举堆内存目前各个年代区域的内存情况
7. 触发GC的时机
(1)当新生代或者老年代满了的时候
(2)手动调用的System.gc()
(3)程序运行时有一条低优先级的GC线程,他是一条守护线程,当这条线程处于运行状态的时候,自然就触发了一次GC了。
8. 内存溢出和内存泄露的区别
(1)内存溢出:指程序在申请内存的时候,没有足够大的空间可以分配了。
(2)内存泄露:程序在申请内存后,没有办法释放掉已经申请到内存,始终占用着内存,即被分配的对象可达但无用。内存泄露一般是因为内存中有一块很大的对象,但是无法释放。内存泄露终将导致内存溢出。一般是老年代中的对象没有被释放的原因
9. 并行和并发的区别
(1)并行Parallel:多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
(2)并发Concurrent:用户线程与垃圾线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上
10. Minor GC和Full GC的区别
(1) 新生代GC(Minor GC)
(2)老年代GC (Major GC/Full GC),一般要比Minor GC慢10倍以上
11. Client模式和Server模式的区别
最先是解释器对.class文件进行解释执行的,某块代码运行的特别频繁时,使用编译器(Just In Timer Compiler,即JIT编译器)编译成与本地平台相关的机器码。
当程序需要迅速启动和执行的时候,解释器可以先发挥作用,省去编译的时间,立即执行
在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越爱越多的代码编译成本地代码之后,可以获得更高的执行效率
12. TLAB(Thread Local Allocation Buffer)
每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲。直接在TLAB上分配的方式称为快速分配方式,而TLAB大小不够,导致内存被分配在Eden区的内存分配方式称为慢速分配方式。
13. 对象优先分配在Eden区上
堆分成两块不同的区域:新生代(Young)和老年代(Old)。新生代(Young)又被划分为三个区域:Eden、From Survivor 和 To Survivor。一般对象优先分配在Eden区
14. 大对象直接进入到老年代
长期存活的对象将进入老年代。Eden区中的对象在一次Minor GC后没有被回收,则对象年龄+1,当对象年龄达到“-XX:MaxTenuringThreshold”设置的值得时候,对象晋升到老年代中;当Survivor空间中相同年龄的所有对象综合大于Survivor空间一半,年龄大于或等于该年龄的对面会直接进入到老年代,无需等到“-XX:MaxTenuringThreshold”设置的年龄要求。