|
| 1 | + ## jvm 的主要组成部分及其作用 |
| 2 | + |
| 3 | +- 类加载器(ClassLoader) |
| 4 | +- 运行时数据区(Runtime Data Area) |
| 5 | +- 执行引擎(Execution Engine) |
| 6 | +- 本地库接口(Native Interface) |
| 7 | + |
| 8 | +组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。 |
| 9 | + |
| 10 | +## jvm 运行时数据区 |
| 11 | + |
| 12 | +- 程序计数器 |
| 13 | + |
| 14 | + 指向当前线程执行的字节码指令的地址( 行号 )。 这样做的**用处是多线程操作时, 挂起的线程在重新激活后能够知道上次执行的位置。** |
| 15 | + |
| 16 | +- 虚拟机栈 |
| 17 | + |
| 18 | + 存储当前线程执行方法时所需要的数据,指令,返回地址。因此一个线程独享一块虚拟机栈 ,栈中内存的单位是栈帧。 每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。 |
| 19 | + |
| 20 | +- 本地方法栈 |
| 21 | + |
| 22 | + 本地方法栈为虚拟机使用到的 Native 方法服务。 |
| 23 | + |
| 24 | +- 堆 |
| 25 | + |
| 26 | + 详见堆内存模型。 |
| 27 | + |
| 28 | +- 方法区 |
| 29 | + |
| 30 | + 用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。 |
| 31 | + |
| 32 | +### 堆 |
| 33 | + |
| 34 | +堆内存模型: |
| 35 | + |
| 36 | + |
| 37 | + |
| 38 | + **此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。** |
| 39 | + |
| 40 | +## 堆和栈的区别 |
| 41 | + |
| 42 | +1. 栈内存存储的是局部变量而堆内存存储的是实体; |
| 43 | + |
| 44 | +2. 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短; |
| 45 | + |
| 46 | +3. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。 |
| 47 | + |
| 48 | +## 什么是双亲委派模型? |
| 49 | + |
| 50 | +如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。 |
| 51 | + |
| 52 | +## 对象的创建过程? |
| 53 | + |
| 54 | +#### Step1:类加载检查 |
| 55 | + |
| 56 | +虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的**类加载过程**。 |
| 57 | + |
| 58 | +#### Step2:分配内存 |
| 59 | + |
| 60 | +在**类加载检查**通过后,接下来虚拟机将为新生对象**分配内存**。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。**分配方式**有 **“指针碰撞”** 和 **“空闲列表”** 两种。 |
| 61 | + |
| 62 | +#### Step3:初始化零值 |
| 63 | + |
| 64 | +内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。 |
| 65 | + |
| 66 | +#### Step4:设置对象头 |
| 67 | + |
| 68 | +初始化零值完成之后,**虚拟机要对对象进行必要的设置**,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 **这些信息存放在对象头中。** |
| 69 | + |
| 70 | +#### Step5:执行 init 方法 |
| 71 | + |
| 72 | +在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。 |
| 73 | + |
| 74 | + ## 类加载的执行过程? |
| 75 | + |
| 76 | +1. 加载:根据查找路径找到相应的 class 文件然后导入; |
| 77 | +2. 检查:检查加载的 class 文件的正确性; |
| 78 | +3. 准备:给类中的静态变量分配内存空间; |
| 79 | +4. 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址; |
| 80 | +5. 初始化:对静态变量和静态代码块执行初始化工作。 |
| 81 | + |
| 82 | + ## 怎么判断对象是否可以被回收? |
| 83 | + |
| 84 | +- 引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题; |
| 85 | +- 可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。 |
| 86 | + |
| 87 | + ## Java 中都有哪些引用类型? |
| 88 | + |
| 89 | +- 强引用 |
| 90 | +- 软引用 |
| 91 | +- 弱引用 |
| 92 | +- 虚引用 |
| 93 | + |
| 94 | + ## jvm 有哪些垃圾回收算法 |
| 95 | + |
| 96 | +- 标记-清除算法 |
| 97 | +- 标记-整理算法 |
| 98 | +- 复制算法 |
| 99 | +- 分代算法 |
| 100 | + |
| 101 | + ## jvm 有哪些垃圾回收器? |
| 102 | + |
| 103 | +- Serial:最早的单线程串行垃圾回收器。 |
| 104 | + |
| 105 | +- Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。 |
| 106 | + |
| 107 | +- ParNew:是 Serial 的多线程版本。 |
| 108 | + |
| 109 | +- Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。 |
| 110 | + |
| 111 | +- Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。 |
| 112 | + |
| 113 | +- CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。 |
| 114 | + |
| 115 | +- G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。 |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | + **新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?** |
| 120 | + |
| 121 | +- 新生代回收器:Serial、ParNew、Parallel Scavenge |
| 122 | + |
| 123 | +- 老年代回收器:Serial Old、Parallel Old、CMS |
| 124 | + |
| 125 | +- 整堆回收器:G1 |
| 126 | + |
| 127 | +新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。 |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | + ## 详细介绍一下 CMS 垃圾回收器 |
| 132 | + |
| 133 | +CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。 |
| 134 | + |
| 135 | +CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。 |
| 136 | + |
| 137 | +## 说一下 jvm 调优的工具 |
| 138 | + |
| 139 | +JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。 |
| 140 | + |
| 141 | +- jconsole:用于对 JVM 中的内存、线程和类等进行监控; |
| 142 | +- jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。 |
| 143 | + |
| 144 | +**常用的 jvm 调优的参数都有哪些?** |
| 145 | + |
| 146 | +- -Xms2g:初始化推大小为 2g; |
| 147 | +- -Xmx2g:堆最大内存为 2g; |
| 148 | +- -XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4; |
| 149 | +- -XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2; |
| 150 | +- –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合; |
| 151 | +- -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合; |
| 152 | +- -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合; |
| 153 | +- -XX:+PrintGC:开启打印 gc 信息; |
| 154 | +- -XX:+PrintGCDetails:打印 gc 详细信息。 |
0 commit comments