1、Java虚拟机结构java
一、Java Virtual Machine:JVM是运行在操做系统之上,与硬件没有直接编程
二、JVM体系结构缓存
三、类装载器ClassLoader服务器
负责加载class文件,class文件在文件开头有特定的文件标示,而且ClassLoader只负责class文件的加载,至于它是否能够运行,则由Exection Engine(执行引擎负责解释命令,提交操做系统执行)决定。多线程
通常:Java 运行代码时,JVM 的整个处理过程以下并发
①.JVM 向操做系统申请内存,JVM 第一步就是经过配置参数或者默认配置参数向操做系统申请内存空间,根据内存大小找到具体的内存分配表,而后把内存段的起始地址和终止地址分配给 JVM,接下来 JVM 就进行内部分配。编程语言
②.JVM 得到内存空间后,会根据配置参数分配堆、栈以及方法区的内存大小。函数
③.class 文件加载、验证、准备以及解析,其中准备阶段会为类的静态变量分配内存,初始化为系统的初始值。高并发
④. 完成上一个步骤后,将会进行最后一个初始化阶段。在这个阶段中,JVM 首先会执行构造器 <clinit> 方法,编译器会在.java 文件被编译成.class 文件时,收集全部类的初始化代码,包括静态变量赋值语句、静态代码块、静态方法,收集在一块儿成为 <clinit>() 方法。性能
类加载过程:
3.1 Class Loader SubSystem
3.1.1 Load
双亲委派:某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,若是父类加载器能够完成类加载任务,就成功返回;只有父类加载器没法完成此加载任务时,才本身去加载。
3.1.2 Link
3.1.3 Init初始化
标记为常量值的字段赋值,以及执行<clinit>方法的过程。JVM会经过加锁来确保类的<clinit>方法仅被执行一次。只有当初始化完成以后,类才正式成为可执行的状态。父类型的初始化逻辑优先于当前类的逻辑。
除被final所修饰的静态字段,剩下的直接赋值操做,以及全部静态代码块中的代码,会被Java编译器至于同一方法中,命名<clinit>。
类初始化被触发的状况:
①当虚拟机启动时,初始化用户指定的主类;
②当遇到用以新建目标类实例的new指令时,初始化new指令的目标类;
③当遇到调用静态方法的指令时,初始化该静态方法所在的类;
④当遇到访问静态字段的指令时,初始化该静态字段所在的类;
⑤子类的初始化会触发父类的初始化;
⑥若是一个接口定义了default方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
⑦使用反射API对某个类进行反射调用时,初始化这个类;
⑧当初次调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类。
四、Native Interface本地接口
Java语言自己不能对操做系统底层进行访问和操做,可是能够经过JNI接口调用其余语言来实现对底层的访问。
本地接口的做用是融合不一样编程语言为Java所用,它的初衷是融合 C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有调用C/C++程序,因而就在内存中专门开辟了一块区域处理标记为Native的代码,它的具体作法是Native Method Stack中登记Native方法,在Execution Engine 执行时加载Native libraries。
五、Native Method Stack
它的具体作法是Native Method Stack中登记native方法,在Execution Engine执行时加载本地方法库。
六、PC寄存器
每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个很是小的内存空间,几乎能够忽略不记。
七、栈
栈也叫栈内存,主管Java程序的运行,是在线程建立时建立,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来讲不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。基本类型的变量、实例方法、引用类型变量都是在函数的栈内存中分配。
如:Exception: java.lang.StackOverflowError
八、方法区
方法区是线程共享的,一般用来保存装载的类的元结构信息。好比:运行时常量池+静态变量+常量+字段+方法字节码+在类/实例/接口初始化用到的特殊方法等。
一般和永久区关联在一块儿(Java7以前),但具体的跟JVM的实现和版本有关。
九、Head堆(Java 7以前)
一个JVM实例只存在一个堆内存,堆内存大小是能够调节的。(-Xms1024m -Xmx1024m -XX:+PrintGCDetails)
类加载器读取了类文件后,须要把类、方法、常变量放到堆内存中,保存全部引用类型的真实信息,以方便执行器执行。
十、新生区
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。
新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace)。
Eden space:全部的类都是在伊甸区被new出来的。
Survivor pace: 0区(Survivor 0 space)和1区(Survivor 1 space)。
当Eden space的空间用完时,程序又须要建立对象,JVM的垃圾回收器将对Eden space进行垃圾回收(Minor GC),将Eden space中的再也不被其余对象所引用的对象进行销毁。而后将Eden space中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,而后移动到1区。那若是1区也满了,再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。若养老区执行了Full GC以后发现依然没法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。
若是出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。缘由有二:
(1)Java虚拟机的堆内存设置不够,能够经过参数-Xms、-Xmx来调整。
(2)代码中建立了大量大对象,而且长时间不能被垃圾收集器收集(存在被引用)。
2、内存溢出
一、stackOverflowError(栈溢出)
线程私有:
①Program Counter Register(PC寄存器):不存在。
②Java Stack:对于不可动态扩展栈大小的线程来讲,不断持续的往栈里面压入栈帧,最后就会发生stackOverflowError
③Native Method Stack:本地方法栈调用本地方法接口,进而调用本地方法库(C或C++编写),也会发生stackOverflowError
二、OutOfMemeryError
2.1 直接内存(Direct Memory)——JVM内存模型外:
java.lang.OutOfMemeryError:Direct buffer Memery:元空间(jdk8)并不在虚拟机中,而是使用本地内存 。NIO程序使用ByteBuffer来读取或者写入,基于通道(Channel)与缓存区(Buffer)的I/O方式,它可使用Native函数库直接分配堆外内存,而后经过一个存储在Java堆里面的DirectByteBuffer对象做为这块内存的引用进行操做。这样能在一些场景中显著提升性能,避免了在Java堆和Native堆中来回复制数据。
ByteBuffer.alloteDirect(capability):分配OS本地内存,不属于GC管辖范围,因为不须要内存拷贝因此速度相对较快。若是不断分配本地内存,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemeryError,那程序就直接崩溃。
2.2 JVM内存模型内
java.lang.OutOfMemeryError:Java heap space:堆内存溢出,最多见。
java.lang.OutOfMemeryError:PerGen space:(jdk8以前)JVM对永久代回收很是不积极,运行时大量动态类生成、相似Intern字符串缓存占用太多。
java.lang.OutOfMemeryError:Metaspace:(jdk8)。
java.lang.OutOfMemeryError:GC overhead limit exceeded:GC回收时间过长,就会抛出OOM,超过98%的时间用来作GC而且回收不了2%的堆内存,连续过次GC,都回收不了2%的状况才会发生。
java.lang.OutOfMemeryError:unable to create new native thread:高并发请求服务器,应用建立了太多线程了,一个应用进程建立多个线程,超过系统承载极限;Linux系统默认容许单个进程能够建立的线程时1024。
解决办法:下降应用程序建立线程的数量,不过应用不须要这么多线程,改代码将线程数量降下来。不然,修改Linux默认1024个线程的限制,扩大值。
https://blog.csdn.net/longgeqiaojie304/article/details/93851827
4、JVM调优
十一、MAT(Eclipse Memory Analyzer)
Eclipse Markerplace中搜索安装Memory Analyzer。
运行时:调出Run Configurations 在Arguments的VM arguments中设置-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError。。。。。。Apply 。。。Run
刷新项目,出现java_pidxxxxx.hprof。。。。