Java——JVM

JVM内部分为三部分:1.类装载器子系统

                                  2.运行时数据区

                                  3.执行引擎

 

类加载器:1.Bootstrap加载器(启动类加载器)

                 2.Extension加载器(扩展类加载器)

                 3.Application加载器(应用程序类加载器)

                 4.用户自定义类加载器
 

双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

    使用双亲委派模型来组织类加载器之间的关系,有一个很明显的好处,就是Java类随着它的类加载器(说白了,就是它所在的目录)一起具备了一种带有优先级的层次关系,这对于保证Java程序的稳定运作很重要。例如,类java.lang.Object类存放在JDK\jre\lib下的rt.jar之中,因此无论是哪个类加载器要加载此类,最终都会委派给启动类加载器进行加载,这边保证了Object类在程序中的各种类加载器中都是同一个类。

 

    JVM启动时会用bootstrap类加载器加载一个初始化类,这个类会在主函数调用之前完成链接和初始化。执行这个方法会执行加载,链接,初始化需要的额外类和接口。

    加载:找到这个类的class文件或根据特定的名字找到接口类型,然后读取到一个字节数组中。接着,这些字节会被解析验证它们是否代表一个类并包含正确的major,minor版本信息,直接类的类和接口也会被加载进来。这些操作一旦完成,类或者接口对象就从二进制中创建出来。

    链接:是校验类或接口并准备类型和父类父接口的过程,分为三步:校验,准备,部分解析。
    校验:这一阶段的目的是为了保证Class文件的字节流信息中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
     准备:正式为类变量分配内存并设置初始值,即在方法区中分配这些变量所使用的内存空间(被final修饰的变量在准备阶段初始化值)。
    解析:将虚拟机常量池的符号引用替换为直接引用的过程。(类信息,类方法信息,类File信息)

 

    执行引擎:或者在执行字节码,或者在执行本地方法。主要的执行技术有:解释,即时编译,自适应优化。每调用一个函数,就为这个函数创建一个栈帧,并加入虚拟机栈。其实就对应了一个栈帧的入栈和出栈。

 

    运行时数据区:方法区,堆,java栈,PC寄存器,本地方法栈。方法区和堆由所有线程共享,java栈和PC寄存器由线程独享。

    堆:存放所有程序在运行时创建的对象以及数组的区域,因为是JVM所有线程共享,在其上进行内存的分配均需要加锁,这也导致new对象开锁开销比较大;

    方法区(持久代):当JVM的类装载器加载class文件,并进行解析,把解析的类型信息放入方法区。存放了所加载类的信息,类中的静态变量,类中定义为final类型的常量,类中的File信息,类中的方法信息;

    java栈:存放的是基本数据类型的数据,局部变量和自定义对象的引用(不是对象,对象都存放在堆区),虚拟机只会对其进行两种操作,以帧为单位的压栈和出栈。每一个帧代表一个方法,JAVA方法有两种返回方式,return和抛出异常,都会导致该方法对应的帧出栈和释放内存。栈的好处是不需要垃圾回收,随线程结束释放内存;

   本地方法栈:存放native方法进入区域的地址;

   PC寄存器:每个线程启动时都会创建一个PC(程序计数器)寄存器,PC寄存器保存的是当前正在执行的JVM指令的地址。每一个线程都有自己的PC寄存器,也是该线程启动时创建的。
 

JVM垃圾回收

    不同的引用类型,GC会采取不同的方法进行回收。

1.强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有被其他对象引用,GC时会被回收);

2.软引用:JAVA中提供的一种比较适合缓存场景的引用(只有在内存不够的情况下才会被GC);

3.弱引用:在GC时一定会被回收;

4.虚引用:虚引用只是用来得知对象是否被GC。

    通常我们说的JVM内存回收总是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指JVM的堆空间,而持久代则是方法区域,不属于Heap。

    Young(年轻代)

    年轻代分为三个区域,一个Eden区,两个Survivor区。大部分对象在Eden区中生成,当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个区域满时,此区域的对象将被复制到另一个Survivior区。当这个区也满了时,从第一个Survivor区复制过来的并且还存活的对象,将被复制到年老区。Survivor的两个区是对称的,没有先后关系,所以同一个区域中可能同时存在从Eden复制过来的对象,和从前一个Survivor复制过来的对象,而复制年老区的只有从第一个Survivor复制过来的对象,而且,Survivor区总有一个是空的。

    Tenured(年老代)

    年老代存放从年轻代存活的对象,一般都是生命周期比较长的对象。

    Perm(持久代)

    用于存放静态文件,如JAVA类,方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,这时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-xx:MaxPermSize=j进行设置。

 

    内存溢出

    堆:不断创建对象导致内存溢出,可以将新建的对象存到list里面去,就不会回收了;

    栈:栈的内存溢出一是申请的不够了,二是JVM内存太小。如果创建一个void方法调用自身,错误是StackoverflowError。如果不断创建线程则会outofMemory。对于第二种多线程内存溢出,通过减小最大堆和栈容量来获取更多的线程。

    方法区和运行时常量池:不断创建类会导致方法区内存溢出,而不断将常量放入到常量池(String.intern()),常量池会内存溢出。

    回收机制:1.分代复制垃圾回收

                     2.标记垃圾回收

                     3.增量垃圾回收

    四种常见的垃圾收集算法:1.标记-清除算法

                                             2.复制算法

                                             3.标记-整理算法

                                             4.分代收集算法

    垃圾收集器:1.Serial收集器

                        2.ParNew收集器

                        3.CMS收集器

    JVM调优

    1.查看堆空间大小分配(年轻代,年老代,持久代分配)

    2.垃圾回收监控(长时间监控回收情况)

    3.线程信息监控:系统线程数量

    4.线程状态监控:各个线程都处在什么样的状态下

    5.线程详细信息:查看线程内部运行情况,死锁检查

    6.CPU热点:检查系统哪些方法占用了大量CPU时间

    7.内存热点:检查哪些对象在系统中数量最大

 

GC次数频繁?

打印GC日志,结合工具查看minor gc(新生代)/major gc(老年代)哪个频繁 

(1)适当增加堆空间内存

(2)选择垃圾收集器不合适,G1适合大内存情况

(3)set pause time设置小一点/堆内存使用率occupancy调大一点

 

CPU持续飙升

-top pid 命令查看内存,jstack查看线程使用情况/死循环,jmap查看堆内存,jinfo查看JVM参数

(1)集群减少并发

(2)mq增加延时异步处理

(3)业务层面 线程死锁

 

OOM

dump文件,分析日志,借助工具



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值