JVM相关

1.JVM组成结构

image

是运行在操作系统之上的,与硬件没有直接的交互

image

线程start()之后并不是立即启动,而是等待CPU和操作系统调度

线程生命周期:NEW(就绪),RUNNABLE(运行),BLOCKED(阻塞),WAITING(等待),TIME_WAITING,TERMINATED(终结);

1 本地库接口:

如:Thread。class下的start0()方法,只有方法头的定义,没有方法体的实现。
image

用native修饰的方法,通过Java native Interface(JNI)用JVM底层的本地库接口去调用操作系统的底层的函数库(C的函数),Java的实例范围到此为止,接下来的事由操作系统,找操作系统底层的本地方法库,Java只是给一个native的标识,说明调这个线程的启动交给操作系统和CPU,不是Java自己。

Java可以通过本地库接口去调用C语言,如同Java调用Mysql,需要Java调用JDBC驱动包才能调用Mysql。

本地方法栈存放那些用native修饰的本地方法,将那些实例栈区分开来,具体做法是在Native Method Stack中登记native方法,在Execution Engine执行的时候加载native libraies(如jdbc驱动包)。

2.Class Loader类加载器

负责加载class文件,class文件在文件开头有特定的文件标识,并且ClassLoader只负责class文件的加载,至于能否运行,则有Execution Engine(执行引擎)决定。

3.Method Area 方法区

方法区是被所有线程共享的,所有字段和方法的字节码,以及一些特殊的方法如构造函数,接口代码也在这里定义,此区域属于共享区间。

JVM优化优化的是共享区间,线程私有的不会优化,99%是优化堆,1%优化方法区。

静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中

4.PC Register 程序计数器

每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一跳指令。

main方法进来Controller调用Service调用Dao,方法调用不会出错就是这些方法加载进来到虚拟的栈中,程序计数器会记住这些方法的加载顺序,执行的时候按指针指向执行,main()->test1()->test2()

5.Native Method Stack 本地方法栈

在本地方法栈中登记native方法,在Execution Engine执行的时候加载native libraies。

栈管运行,堆管内存

6. stack 栈

  • 1.Stack是什么?

也叫栈内存,主管Java程序的运行,是在线程创建时创建的,线程结束栈内存也释放了,它的生命期是随线程的生命期,对于栈来说不存在垃圾回收问题,只要线程结束栈就被释放了,是线程私有的。基本类型的变量和对象的引用变量就是在函数的栈内存中分配的

  • 2.栈存储什么?

栈帧中主要存储三类数据:

1.本地变量:输入参数和输出参数以及方法内的变量。

2.栈操作:记录出栈,入栈的等操作。

3.栈帧数据:包括类文件,方法的等。

  • 3.栈运行原理:

image

栈中的数据都是以栈帧的格式存在,是一个内存区域,一个有关方法和运行期间的数据集,当一个方法A被调用时就产生了一个栈帧F1,压入到栈中,A方法有调用了B方法,于是产生栈帧F2压入到栈中,B方法调用了C方法,于是产生了栈帧F3压入到栈中......

执行完毕后,先弹出栈帧F3,再是F2栈帧,再是F1栈帧......

JVM优化是哪里?

image

7.堆

堆内存示意图:
image

  • 7.1新生区是类诞生,成长,消亡的地方,新生区有分为两部分:Eden区和Survivor区,所有的类都在Eden区被new出来,幸存区又分为两个:0区和1区,Eden区空间用完时,又需要创建对象时,JVM的垃圾回收器会对Eden区进行垃圾回收(Minor GC),将那些不在被其他对象引用的对象进行垃圾回收,Eden剩余的存活对象被移动到S0区,若S0区也满了,再对该区进行垃圾回收,然后移动到S1区,若S1区也满了,就会移动到老年代,,若老年代也满了,就会进行Major GC(Full GC),进行老年代的内存清理。

若产生了 java.lang.OutOfMemorError:Java heap space异常,说明java虚拟机的堆内存不够,产生的原因可能有两个:

1.Java虚拟机的堆内存设置不够,可以用过 -Xms,-Xmx来设置。

2.代码中创建了大对象,并且长时间不能被垃圾回收器回收(存在被引用)。

一个JVM实例只有一个堆内存,堆内存的大小是可以调节的

  • 7.2.老年代用来保存聪新生代筛选出来的Java对象,一般池对象都在这里活跃。

  • 7.3.永久代:没有垃圾回收,加载一些构建系统不能被回收且保证系统能稳定运行的元元素。是一个常驻的内存区域,用于存放JDK自身锁携带的Class,Interface的元数据,它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收掉的,关闭JVM才会释放此内存区域。

若出现:java.lang.OutOfMemoryError:PermGen space 说明是Java虚拟机对永久代Perm的内存设置不够,一般是因为程序启动需要加载大量的jar包,或者大量动态反射生成的类不断被装载。最终导致Perm区域被占满。

Jdk1.6及以前:有永久代,常量池1.6在方法区

Jdk1.7:有永久代,已经逐步“去永久代”,常量池1.7在堆

Jdk1.8:无永久代,常量池1.8在元空间。

程序内存划分小结

JDK 7

image

在第五行调用foo()方法时,foo方法被压入栈中,foo()方法中的Object类型的 param引用变量会指向 obj->new Object()。第七行的param.toString()就是Object.toString(),toString()方法底层会生成一个新的String对象,
image
image
new 的是一个char类型的buf数组,会在字符串常量池中生成。

image

方法和堆一样,是各个线程共享的内存区域,用来存放虚拟机加载的:类信息+普通常量+静态常量等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但他还有一个别名叫Non-Heap(非堆),目的就是要和堆分开。

对于HotSpot虚拟机,很多开发者将方法区成为永久代,但实际上两者有本质区别,或者说用永久代来实现方法区而已,永久代只是方法区(相当于是一个Interface)的一个实现,jdk1.7已经将放在永久代的字符串常量池中移走。

常量池是方法区的一部分,Class文件除了有类的版本,字段,方法,接口等描述信息以外,还有一项就是常量池,这部分将在类加载后进入方法区的运行时常量池存放。

GC

频繁收集Young区

较少收集Old区

基本不动Perm区

GC算法总体概述:

JVM在进行垃圾回收时,并非是正对以上三个区域一起回收,大部分回收指的是新生代。
因此GC按照回收的区域有分为两种类型:一种是普通GC(minor gc),一种是全局的GC(major gc or Full gc),

普通GC:只针对新生代的GC。
全局GC(major gc or Full gc):针对老年代的gc,偶尔也伴随着新生代的GC以及对永久代的GC。

image

年轻代中使用的是minor gc,这种gc采用的算法是复制算法(Copying)。

minor gc会把eden中的所有的存活的对象都移到Survivor区,如果Survivor区放不下,那么剩下的存活的对象就被移到Old generation中,也就是一但收集后,Eden区就变为空的了。 当对象在Eden区(包括一个Survivor区,假设这里是s0区)出生后,进过一次minor gc后,如果还存活,并且能被另外一块survivor区所容纳(假设这里为s1区),则使用复制算法将这些任然存活的对象复制到另外一块survivor区(s1区),然后清理Eden区和s0区,并且将这些对象的年龄设置为1,以后对象每在Survivor区熬过一次minor gc,对象的年龄就会+1,当对象的年龄达到某个值(默认是15,通过-XX:MaxTenuringThreshold来设定参数),这些对象就会移到老年代。

老年代一般是由标记-清除(Mark-Sweep)或者是标记整理(Mark-Compact)的混合实现。

标记-清除:
image

image

posted @ 2021-09-18 12:16  Chcode  阅读(108)  评论(0)    收藏  举报