JVM复习

  1.   JVM虚拟机的启动
  2. JVM虚拟机的执行
  虚拟机指令
//编辑Java文件 虚拟机指令
javap -v 类名.class
//查看程序执行  进程
jps
 

  

简略版jvm虚拟机

3.JVM 虚拟机的 栈 特点
跨平台性、指令集小、指令多;职能g'xing

  

4.加载器

  4.1系统加载器
        java的核心类库,使用系统加载器
    4.2引导类加载器
        获取 加载器时为null
    4.3自定义加载器
        什么样的文件加载什么样的文件

  

 5.双亲委派机制

jvm对calss文件 ,是一个按需加载机制。什么时候需要什么时候加载

自定义加载器 → 系统类加载器 → 扩展类加载器 → 引导类加载器

  

6.反向委派机制

加载第三方类的时候   核心类提供接口 → 执行引导类加载  → 线程上下文加载器(默认执行:系统类加载器) →  执行第三方jar包
优势:
    1.避免类的重复加载
    2.保护程序安全,防止核心api被篡改

  

7.运行时数据区

    7.1程序计数器 (pc寄存器)  (可以理解为 游标或者 迭代器)
           作用:来储存下一条指令 地址的  (占内存空间很小)
            特点:属于线程私有,生命周期与线程保持一致。
                        程序的  分支、循环、跳转、异常处理、线程回复,都需要他来执行。
                        jvm唯一个没有内存溢出 的 区域。
     7.2 为什么使用pc寄存器 储存字节码指令??
            答:因为cpu需要不停切换各个线程,当他切换到要执行线程时,就时候需要jvm的字节码解释器 通过 pc寄存器的值来明确下一条指令。
    7.3pc寄存器为什么设定为线程私有?
            为了能准确地记录各个线程正在执行的字节码指令地址,最好的方法自然是每个线程分配一个pc寄存器。独立计算,不会出现干扰。
    7.4 栈管运行堆管存储
    7.5 栈中常见异常
        1.StackOverFlowError  (如果 jvm虚拟机设置了 固定的栈大小,当运行程序超出,设置的大小就会报StackOverFlowError (栈空间溢出))
        2.OutOfMemoryError (如果jvm虚拟机栈可以动态扩展,但是扩展时无法申请到足够的内存,或者创建创建虚拟机栈,没有足够的内存,就会报OutOfMemoryError)

  

8.虚拟机栈(栈管运行,队管存储)

  8.1 java虚拟机栈,其内部保存一个个栈帧(Stack Frame)(栈帧是栈的最小储存单元)
一个线程对应一个java虚拟机栈   (栈不存在垃圾回收问题, 但是存在内存溢出)
栈:  先进后出
队列:先进先出
8.2 栈中储存 一个线程对应一个java虚拟机栈 每个方法队形一个栈帧
8.3栈的内部结构 局部变量表 局部变量表中的变量只在当前方法调用中有效。 当方法调用结束后,随着方法栈的销毁,局部变量表也会随之销毁。 局部变量表 会存放方法的变量, //在静态方法当中,不允许引用this。原因:this变量不存在当前变量表中 //实例方法中可以引用this, 实例对象提供this变量的Slot访问索引 操作数栈 入栈/出栈 方法返回地址 动态链接 一些附加信息 帧数据区(方法返回地址、动态链接、一些附加信息)
8.4 栈顶缓存技术(hotspot虚拟机) 8.5方法区 方法区是一个非常重要的区域,也是被线程共享的区域,方法区存储了每个类的信息(类的名称、方法信息、字段信息),静态变量、常量以及编译后的代码等。 方法区还包括一个常量池,用来存储编译期间生成的字面量和符号引用。这部分内容在类被加载后,都会存储到方法区中的RCP。值得注意的是,运行时产生的新常量也可以被放入常量池中,比如 String 类中的 intern() 方法产生的常量。 常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用.
8.5.1常量池作用 常量池的作用:就是为了提供一些符号和常量,便于指令的识别。(高可用,使得编译文件较小) 本地方法接口(本地方法库) //java虚拟机 需要调用C库,所存在一些方法 本地方法接口:包括对操作系统对接 本地方法栈 java虚拟机用于管理java方法的调用 本地方法栈,是私有的。

  

回顾
方法 -- > 栈帧 -->    局部变量
                        -->    方法返回地址
                        -->    操作数栈
                        -->    动态链接
                        -->    一些附加信息

  

面试问题

1.栈溢出的情况(stackOverflowError)
    答:设置栈大小,运行时超出设置值
        //不设置栈大小,运行时超出物理内存值    (内存溢出)
3.栈余越大越好么???
    答:他会挤占运行时区 其他区域的空间

  2.垃圾回收会涉及虚拟机栈么?  答案:如以下表格

 
GC
Error
程序计数器
×
×
虚拟机栈
×
本地方法区
×
方法区

什么是TLAB

//
1.一个进程对应一个方法区和堆。
2.将堆内存分成小块,每个线程占用一小块内存堆。就形成了TLAB。
3.方法结束后,堆中对象不会马上转移,

  关系图

堆空间的内部结构

java7 以及之前堆内存逻辑分为三部分:新生区+养老区+永久区

  

java8和之后版本 内存逻辑分为:新生区+养老区+原空间
相较于以前版本,永久区 变为 原空间

设置堆空间的大小
-Xms    表示堆起始内存
-Xmx    表示堆区的最大内存
//堆的OOM

  

年轻代和老年代

 优先在伊甸园区分配,

堆内存 --> 年轻代 --> 伊甸园、S0、S1的对象分配
YGC过程,伊甸园区空间充满时,会触发YGC
常见调优工具

Minor GC、Major GC、Full GC

(jvm调优 可简单的理解为 执行GC次数少一些)
Minor GC 完全等价与  YGC(年轻代)
老年代区域 执行  Major GC
Full GC :整个java堆,方法区的垃圾收集
Major 和 Full GC 执行时间 相较于 Minor GC时间较长
JVM进行GC,并非每次都是对三个内存区域一起回收,大部分时候回收是 指新生代。(三部分指:新生代、老年代、方法区)

年轻代触发机制(Minor GC)
1.当年轻代空间不足,就会触发Minor GC(年轻代中 Edem区满了,才会触发GC。 S1,S0不会触发GC)
2.Minor GC 触发频率高
3.Minor GC 会触发STW(停止其他用户线程),等垃圾回收结束,才会继续执行。

老年代GC(Major GC/Full GC)
1.当对象从老年代中消失,Major GC 或 Full GC 执行发生了
2.执行Major GC 经常会执行Minor GC(非绝对),(当老年代空间不足则先触发Minor GC,如果还是不足则触发Major GC)
3.Major GC的速度会很慢,STW的时间会很长。
4.Major GC之后空间还是不足,就会抛出异常(OOM)内存溢出

触发Full GC的五种情况
1.调用System.gc(),系统建议执行Full GC。(非必然执行)
2.老年代空间不足
3.方法区空间不足
4.通过Miner GC后进入老年代的平均大小大于老年代的空用内存(标记 没理解)
5.当创建对象Eden区空间不足,转移到S0,S1同样出现空间不足,直接晋升到老年代,老年代空间不足就触发Full GC

//打印GC在虚拟机中的指令
-Xms9m -Xmx9m -XX:+PrintGCDetails

字符串常量池存在堆空间

针对不同年龄的对象分配原则:
1.优先分配Eden区
2.大对象直接分配到老年代(尽量避免出现大对象)
3.长期存活的对象分配到老年代

堆空间参数设置
-XX:+PrintFlagsInitial : 查看所有默认参数的初始值
-XX:+PrintFlagFinal : 查看所有参数的最终值
-Xms: 初始堆空间内存(默认是物理内存的1/64)
-Xmx: 最大堆空间内存(默认是物理内存的1/4)
-Xmn: 设置新生代的大小。(初始值)
-XX:NewRatio: 配置新生代与老年代在堆结构的占比
-XX:SurvivorRatio: 设置新生代中Eden和S0/S1空间的比例
-XX:MaxTenuringThreshould: 设置新生代垃圾的最大年龄
-XX:PrintGCDetails: 输出详细的GC处理日志
-XX:HandlePromotionFailure:是否设置控件分配担保

  

代码优化
1.栈上分配:将堆分配。在方法new出对象,并返回结果。
-Xms256m -Xmx256m -XX:DoEscapeAnalysis -XX:+PrintGCDetails

2.同步省略
3.分离对象或标量替换

  

原文地址:https://www.cnblogs.com/money131/p/13875447.html