JVM的位置
在操作系统之上,可以想象成一个软件,Java程序都运行在上面
JVM结构图
JVM调优的位置
99%的调优在堆中,极少数在方法区中
很多第三方插件都是在执行引擎那块地方做出修改而来,比如Lombook在程序运行时动态生成get/set方法就是一个例子
类加载器:加载.class文件
Java核心的类库是Rt.jar可以解压缩里面有许多class文件
双亲委派机制
该机制是用来保证程序安全的
委派流程:
1.类加载器收到类加载的请求!
2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到到达启动类加载器(向上检索,向下执行)APP-EXC-BOOT
3.启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常(CLassNotFound~),通知子加载器进行加载
4.重复步骤3
Eg:可以看到,明明有main方法却报错显示没找到,这是因为双亲委派机制的存在,我的类名为String,BootStrap加载到了java.lang.String类(也就是rt.jar中的那个),而那个类里面没有main方法,也就是说根本没有加载到自己写的String类,所以会出现下面的异常。因此,这个事情告诉我们不要乱给类命名
为什么双亲委派机制能保证程序的安全,通俗来讲就是有了“双亲”的监督让程序更加的规范,符合JVM的环境(JRE)
Native
native :凡是带了native 关键字的,说明java的作用范围达不到了,会去调用底层c语言的库!会进入本地方法栈
通过JNI调用本地方法本地接口
JNT作用:扩展ava的使用,融合不同的编程语言为ava所用!最初就有: C、C++。
Java诞生的时候C、C++横行,想要立足,必须要有调用c、C++的程序~
它在内存区域中专门开辟了一块标记区域:Native Method Stack,登记native方法
在最终执行的时候,加载本地方法库中的方法通过JNI
Java程序驱动打印机,管理系统,这些都需要去调用本地方法。在企业级应用中较为少见!
Eg:调用其他接口的方式有Socket、web Service、HTTP、Restful、Rpc
Native Method Stack
它的具体做法是Native Method Stack 中登记native方法,在( Execution Engine )执行引擎执行的时候加载Native Libraies。【本地库】
PC寄存器
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
方法区
Method Area方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
栈
栈内存主管程序的运行,生命周期和线程同步;当线程结束,栈内存也就释放了,对于栈来说不存在垃圾回收问题
栈内存中主要存储:八大基本类型+对象引用+实例的方法
先进后出,main()方法最先执行,在栈底所以是最后出栈。
递归造成栈溢出的原因就是:反复的互相调用方法不断地入栈,并且执行的方法不能得到结束无法出栈。使得调用的方法越来越多造成栈溢出
三种JVM
sun - HotSpot,BEA - JRocket,IBM J9 VM
堆
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量,变量~,保存我们所有引用类型的真实对象;
堆内存中还要细分为三个区域:
1.新生区:类诞生和成长的地方,甚至死亡。其中的伊甸园区,是所有的new对象发生的地方。
2.养老区
3.永久区
主要存放加载的Class类级对象如class本身,method,field等等
幸存区0和1在堆内存中是一个动态变化的情况他是伊甸园区和养老区之间的一个过渡,顾名思义无法幸存的对象会被“kill”,GC垃圾回收主要在新生区中的伊甸园区和养老区进行。回收的方式分为轻GC与重GC,其中轻GC主要存在于新生区,只有当进入老年区的垃圾对象超过限额才会触发重GC(full GC),该操作主要存在于养老区。
在开发中可能遇到OOM的问题,表示内存不够,这个内存也就是JVM的堆内存,比如下面这个例子:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
99%的对象都是临时对象,所以经过GC之后很少有进入到老年区中,因此OOM的情况也很少发生
永久区的演变
这个区域常驻内存的。用来存放IDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭VM虚拟机就会释放这个区域的内存
jdk1.6之前︰永久代,常量池是在方法区;
jdk1.7︰永久代,但是慢慢的退化了,去永久代,常量池在堆中
jdk1.8之后:无永久代,常量池在元空间
永久区内存崩溃的情况:一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载。直到内存满,就会出现OOM;
默认情况下:分配的总内存是电脑内存的四分之一,初始化的内存是电脑内存的六十四分之一
但是这些内存参数是可以人为调整的
可以看出,元空间逻辑上存在,但物理上并不存在
出现OOM如何去排查
可以使用内存快照工具分析对象,主要有:MAT(Eclipse),Jprofile(IDEA)两种工具。
向VM传入以下参数即可
当程序运行出现OOM,进入类所在的文件目录下去寻找Jprofile生成的hprof文件即可。
双击打开即可初步发现占用大量内存的对象,俗称“大对象”
查看线程(Thread Dump)即可发现程序中出现问题的所在行。
调优参数: