深入理解java虚拟机:笔记

1.运行时数据区域

1.程序计数器

当前线程执行字节码的行号指示器,字节码解释器工作通过改变这个计数器的值来选取下一条需要执行的字节码指令,每一个线程拥有独立的程序计数器,线程私有的内存

2.虚拟机栈

线程私有的内存,生命周期与线程相同,没有方法被执行会创建一个栈帧,存储局部变量表,操作栈等信息。
每一个方法被调用直至完成,对应一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表所需的内存空间在编译器完成分配,调用方法时在帧中分配多大的局部变量完全确定,运行时不会改变该表大小。
线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
若虚拟机栈动态扩展时无法申请到足够内存则会抛出OutOfMemoryError异常。

3.本地方法栈

与虚拟机栈作用类似,虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的native方法服务

4.java堆

被所有线程共享的内存区域,虚拟机启动时创建,存放对象实例:所有对象实例及数组在堆上分配(JIT编译发展已不是那么绝对)。
java堆是垃圾收集器管理的主要区域,也被称为gc堆,可以根据分代收集算法细分为:新生代,老年代.也可以分为Eden空间、From Survivor空间、To Survivor空间.从内存分配分多个线程私有的分配缓存区。
java堆可以处于物理上不连续的内存空间中,逻辑上可以连续,可以扩展(通过-Xmx 与-Xms控制)。
无法扩展时抛出OutOfMemoryError异常。

5.方法区(hotSpot虚拟机使用永久代来实现方法区)

与java堆一样时各个线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态常量、及时编译器编译后的代码等数据。
垃圾回收在这个区域比较少出现,回收目标主要针对常量池的回收和对类型的卸载,回收条件苛刻,但比较有必要。
无法满足内存分配需求时抛出OutOfMemoryError异常。

运行时常量池

方法区的一部分。Class文件编译期生成的各种字面量和符号引用,类加载后存放到方法区的运行时常量池中。
除了保存class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行常量池中。
运行时常量池相对class文件常量池具有动态性,运行期间也可能将新的常量放入池中。
无法满足内存分配需求时抛出OutOfMemoryError异常。

6.直接内存

并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是会频繁使用,当本机总内存不足时也会抛出OutOfMemoryError异常。
jdk中新加如的nio 可以使用native函数库直接分配堆外内存,然后通过存储在java队中的directByteBuffer对象作用这块内存的引用进行操作。

程序计数器\虚拟机栈\本地方法栈 三个区域随线程而生,随线程而灭.java堆和方法区内存分配回收动态的。

2.对象访问

不同虚拟机不访问对象方式不同:

使用句柄访问

java堆中划分内存作为句柄池,reference存储对象句柄地址,句柄包含实例数据(实例池)和类型数据(方法区)的具体地址信息。

使用指针访问(hotSpot虚拟机)

java堆放置访问类型数据的相关信息,reference中直接存储对象地址。
句柄访问好处是reference中存储稳定的句柄地址,对象移动(常见垃圾收集时)值会改变句柄中的实例指针。
指针访问好处是访问快,节省指针定位的时间开销。

3.垃圾收集器与内存分配策略:

gc成为系统达到更高并发量的瓶颈时,需要手动调节及监控。

判断对象是否存活

1.引用计数算法:对象添加引用计数,有引用它时,计数加1,引用失效,计数减1,计数为0时不被使用;但是难以解决循环引用。
2.根搜索算法:GC root对象作为起点,向下搜索,搜索走过的路径称为引用链,当一个对象到gc root不可达,则可以回收。
gc root对象包括以下几种:虚拟机栈(栈帧中的本地变量表)、方法区(类静态属性、常量)、本地方法栈JNI(native方法)中的引用的对象。

四种引用:

强引用(不会回收被引用对象);

软引用(发生溢出异常前列入回收范围之中),SoftReference;

弱引用(被引用对象生存到下次垃圾收集前),WeakReference;

虚引用(不会对其生存时间构成影响,无法通过虚引用取得对象实例,目的收集器回收时收到通知),PhantomReference;

对象回收:经历两次标记过程

发现无引用链,第一次标记并筛选是否必要执行finalize()方法,若有必要则放置F-Queue队列中,由虚拟机创建低优先级线程执行。
GC对F-Queue队列中的对象第二次标记,若此次标记移除,则可以逃脱。
finalize()适合使用的“关闭外部资源”的工作,使用try-finally或者其他方式使用更好。
方法区回收(hotSpot虚拟机永久代)主要回收:废弃常量和无用的类。

垃圾收集算法

标记-清除算法:先标记需要回收的对象,标记完成后统一回收掉被标记的对象。

效率问题,标记清除过程效率不高。
空间问题,清除后产出大量不连续内存碎片。

复制算法,在标记清除算法上解决效率问题(新生代)

将内存按容量划分为相等两块,每次使用其中一块,当这一块内存用完后,将还存活着的对象复制到另外一块上,再把已使用过的内存空间清理掉。
回收新生代,新生代回收并不是按照1:1划分内存,将内存分为较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。
当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另外的Survivor上,最后清理掉Eden和刚才用过的Survivor的空间(hotSpot虚拟机默认Eden和Survivor比例8:1)。

标记-整理算法,(老年代)

在标记清除算法上,标记后不直接清理可回收对象,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存

分代收集算法

根据对象的存活周期的不同将内存划分为几块,一般把java堆划分为新生代和老年代,这样可以根据各个年代的特点采用最适当的收集算法

垃圾收集器,不同虚拟机不同版本可能会有很大差别

Serial收集器,最基本最古老收集器,新生代收集器,单线程,简单高效,但是会停顿其他线程(Stop the world),直至收集结束。

ParNew收集器,新生代收集器,serial收集器多线程版,会暂停用户线程,但多线程进行GC回收。

Parallel Scavenge收集器,新生代收集器,使用复制算法,并行的多线程收集器,其主要目标是达到一个可控制的吞吐量,停顿时间越短越适合需要与用户交互的程序,而高吞吐量则可以高效利用cpu时间,适合后台运算而无需太多交互的任务。

Serial Old收集器Serial收集器老年代版本,单线程,使用标记整理。

Parallel Old收集器,老年代版本,多线程和标记整理算法,GC多线程且与用户线程同时进行。

CMS收集器,以获取最短回收停顿时间为目标的收集器,重视服务响应速度,基于标记-清除算法。

  四步骤,初始标记、并发标记、重新标记、并发清除。优点:并发收集、低停顿。缺点:对cpu资源敏感、无法处理浮动垃圾、收集结束后会产生大量空间碎片。

G1收集器,基于标记-整理算法,不会产生空间碎片,可以精确控制停顿,收集范围为整个Java堆。
  将java堆划分为多个大小固定的独立区域,跟踪区域内垃圾堆积程度,维护一个优先列表,根据允许的收集时间,优先回收垃圾最多的区域。

内存分配与回收策略

对象优先在java堆中的新生代Eden区中分配,若空间不足,发起一次Minor GC。
大对象(需要大量连续内存空间的java对象)直接进入老年代,避免Eden区及两个Survivor区发生大量复制。
长期存活的对象将进入老年代,虚拟机给每个对象定义了年龄计数器,每过一次Minor GC加一,默认加至15,将该对象晋升至老年代。
动态年龄判断,如果在Survivor空间相同年龄所有对象大小的综合大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

4.虚拟机性能监控与故障处理工具

命令行工具

jps:

列出正在运行的虚拟机进程,并显示虚拟机执行主类,及这些进程的本地虚拟机唯一id,例如:

D:\java\bin>jps
10768 RemoteMavenServer
16224 Jps
11080
jstat:

显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,例如:

            D:\java\bin>jstat -gc 10768
                S0C    S1C      S0U    S1U      EC       EU        OC         OU       MC      MU          CCSC   CCSU     YGC     YGCT    FGC    FGCT     GCT
                48128.0 512.0    0.0    0.0   147456.0 11172.5   175104.0   10777.7   28032.0  26797.1     3456.0   3065.7   43      0.591    31    1.066    1.657
jinfo:

实时查看和调整虚拟机各项参数,例如查询pid为11996的各项参数:

D:\java\bin>jinfo -flags 11996
jmap:

用于生成堆转储快照(heapdump或dump文件),还可以查询finalize执行队列,堆和永久代详细信息,例如生成正在运行idea的dump快照文件:

            D:\java\bin>jmap -dump:format=b,file=idea.bin 11996
            Dumping heap to D:\java\bin\idea.bin ...
            Heap dump file created
jhat:

虚拟机堆转储快照分析,分析jmap生成的dump文件,例如分析上述例子中生成的idea.bin文件

                D:\java\bin>jhat idea.bin
                Reading from idea.bin...
                Dump file created Thu Oct 10 13:59:41 CST 2019
                Snapshot read, resolving...
                Resolving 356454 objects...
                Chasing references, expect 71 dots...............
                .........................
                Eliminating duplicate references.................
                .......................
                Snapshot resolved.
                Started HTTP server on port 7000
                Server is ready.

然后在浏览器中输入http://localhost:7000/查看

jstack:

java堆栈跟踪工具,用于生成虚拟机当前时刻的线程快照。线程快照就是当期虚拟机内每一条线程正在执行的方法堆栈集合,定位线程出现长时间停顿的原因,例如:

D:\java\bin>jstack -l 11996

可视化工具

JConsole:Java监视与管理控制台

内存监控:用于受收集器管理的虚拟机内存(java堆和永久代)的变化趋势,可视化jstat命令
线程监控:可视化jstack命令

jvisualvm:多合一故障处理工具,一个代替上面所有

猜你喜欢

转载自www.cnblogs.com/lantuanqing/p/11648595.html