Skip to content

Commit b29b228

Browse files
committed
update Java Memory Model
1 parent 59eab6a commit b29b228

File tree

4 files changed

+154
-125
lines changed

4 files changed

+154
-125
lines changed

JavaKnowledge/JVM垃圾回收机制.md

+1-31
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# JVM垃圾回收机制
22

3-
4-
5-
## JVM内存模式
6-
7-
JVM内存模型可以分为两个部分,如下图所示,**堆和方法区是所有线程共有的**,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。
3+
[Java内存模型](https://github.com/CharonChui/AndroidNote/blob/master/JavaKnowledge/Java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.md)如下图所示,**堆和方法区是所有线程共有的**,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。
84

95
![Image](https://raw.githubusercontent.com/CharonChui/Pictures/master/jvm.png)
106

@@ -43,32 +39,6 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Gar
4339

4440

4541

46-
### 方法区(Method Area)
47-
48-
方法区也称”永久代“,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB(64位JVM由于指针膨胀,默认是85M),可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。它是一片连续的堆空间,永久代的垃圾收集是和老年代(old generation)捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。不过,一个明显的问题是,当JVM加载的类信息容量超过了参数-XX:MaxPermSize设定的值时,应用将会报OOM的错误。参数是通过-XX:PermSize和-XX:MaxPermSize来设定的。
49-
50-
51-
52-
### 虚拟机栈(JVM Stack)
53-
54-
描述的是java方法执行的内存模型:每个方法被执行的时候都会创建一个”栈帧”,用于存储局部变量表(包括参数)、操作栈、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程****声明周期与线程相同,是线程私有的。栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区被组织为以一个字长为单位、从0开始计数的数组,和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的,可以看作为临时数据的存储区域。除了局部变量区和操作数栈外,java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在java栈帧的帧数据区中。
55-
56-
局部变量表: 存放了编译器可知的各种基本数据类型、对象引用(引用指针,并非对象本身),其中64位长度的long和double类型的数据会占用2个局部变量的空间,其余数据类型只占1个。局部变量表所需的内存空间****编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量是完全确定的,在运行期间栈帧不会改变局部变量表的大小空间。
57-
58-
59-
60-
### 本地方法栈(Native Stack)
61-
62-
与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务(栈的空间大小远远小于堆)。
63-
64-
### 程序计数器(PC Register)
65-
66-
最小的一块内存区域,它的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令****分支****循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。
67-
68-
### 直接内存
69-
70-
直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小.
71-
7242

7343

7444
## 如何判断对象是垃圾

JavaKnowledge/JVM架构.md

+43-33
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Java源代码会被JDK内置的Java编译器(javac)编译成称为字节码(即.
1010

1111
我们大多数人都知道Java的上述故事,这里的问题是该过程中最重要的组成部分-JVM被当作一个黑匣子教给我们,它可以神奇地解释字节码并执行许多运行时活动,例如JIT(程序执行期间进行实时)编译和GC(垃圾收集)。
1212

13-
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/JVM_Archite cture.png)
13+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/JVM_Architecture.png)
1414

1515
## Class Loader子系统
1616

@@ -23,47 +23,47 @@ JVM驻留在内存(RAM)上。在执行过程中,会使用Class Loader子系统
2323
- 当字节码静态引用一个类时
2424
- 当字节码创建一个类对象时(例如: Person person = new Person("John"))
2525

26-
有3种类型的类加载器(与继承属性连接),它们遵循4个主要原则
26+
有3种类型的类加载器:
2727

28-
- 可见性原则
28+
- Bootstrap Class Loader
2929

30-
该原则指出子类加载器可以看到父类加载器加载的类,但是父类加载器找不到子类加载器加载的类
30+
加载来自rt.jar的标准JDK类,例如引导路径中存在的核心Java API类-$ JAVA_HOME/jre/lib目录(例如java.lang。*包类)。它以C / C ++之类的本地语言实现,并是Java中所有类加载器的父级
3131

32-
- 唯一性原则
32+
- Extension Class Loader
3333

34-
该原则指出,父类加载的类不应再由子类加载器加载,并确保不会发生重复的类加载
34+
将类加载请求委托给其父类Bootstrap,如果不成功,则从扩展路径中的扩展目录(例如,安全扩展功能)中加载-JAVA_HOME/jre/ext或java.ext.dirs系统指定的任何其他目录的类。该类加载器由Java中的sun.misc.Launcher$ExtClassLoader类实现
3535

36-
- 委托层次结构原则
36+
- System/Application Class Loader
3737

38-
为了满足上述2个原则,JVM遵循一个委托层次结构来为每个请求装入的类选择类加载器。首先从最低的子级别开始,Application Class Loader将接收到的类加载请求委托给Extension Class Loader,然后Extension Class Loader将该请求委托给Bootstrap Class Loader。如果在Bootstrap路径中找到了所请求的类,则将加载该类。否则,该请求将再次被转移回Extension Class Loader加载器中从该Loader的扩展路径或自定义指定的路径中查找类。如果它也失败,则请求将返回到Application Class Loader中从该Loader的System类路径中查找该类,并且如果Application Class Loader也未能加载所请求的类,则将获得运行时异常— java.lang.ClassNotFoundException
38+
从系统类路径加载应用程序特定的类,可以使用-cp或-classpath命令来在程序执行时动态设置。它在内部使用映射到java.class.path的环境变量。该类加载器由sun.misc.Launcher$AppClassLoader类用Java实现
3939

40-
- 无卸载原则
40+
除了上面讨论的3个主要的类加载器,程序员还可以直接在代码本身上创建用户定义的类加载器。这通过类加载器委托模型保证了应用程序的独立性。这种方法用于Tomcat之类的Web应用程序服务器中,以使Web应用程序和企业解决方案独立运行。
4141

42-
即使类加载器可以加载类,但是无法卸载已加载的类。替代卸载功能的是可以删除当前的类加载器,并创建一个新的类加载器
42+
每个类加载器都有自己的名称空间来保存已加载的类。当类加载器加载类时,它将基于存储在名称空间中的完全合格的类名称(FQCN:Fully Qualified Class Name)搜索该类,以检查该类是否已被加载。即使该类具有相同的FQCN但具有不同的名称空间,也将其视为不同的类。不同的名称空间意味着该类已由另一个类加载器加载
4343

44-
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/java_class_loaders.png)
44+
它们遵循4个主要原则:
4545

46-
#### 类加载器类型
46+
- 可见性原则
4747

48-
- Bootstrap Class Loader
48+
该原则指出子类加载器可以看到父类加载器加载的类,但是父类加载器找不到子类加载器加载的类。
4949

50-
加载来自rt.jar的标准JDK类,例如引导路径中存在的核心Java API类-$ JAVA_HOME/jre/lib目录(例如java.lang。*包类)。它以C / C ++之类的本地语言实现,并是Java中所有类加载器的父级。
50+
- 唯一性原则
5151

52-
- Extension Class Loader
52+
该原则指出,父类加载的类不应再由子类加载器加载,并确保不会发生重复的类加载。
5353

54-
将类加载请求委托给其父类Bootstrap,如果不成功,则从扩展路径中的扩展目录(例如,安全扩展功能)中加载-JAVA_HOME/jre/ext或java.ext.dirs系统指定的任何其他目录的类。该类加载器由Java中的sun.misc.Launcher$ExtClassLoader类实现。
54+
- 委托层次结构原则
5555

56-
- System/Application Class Loader
56+
为了满足上述2个原则,JVM遵循一个委托层次结构来为每个请求装入的类选择类加载器。首先从最低的子级别开始,Application Class Loader将接收到的类加载请求委托给Extension Class Loader,然后Extension Class Loader将该请求委托给Bootstrap Class Loader。如果在Bootstrap路径中找到了所请求的类,则将加载该类。否则,该请求将再次被转移回Extension Class Loader加载器中从该Loader的扩展路径或自定义指定的路径中查找类。如果它也失败,则请求将返回到Application Class Loader中从该Loader的System类路径中查找该类,并且如果Application Class Loader也未能加载所请求的类,则将获得运行时异常— java.lang.ClassNotFoundException。
5757

58-
从系统类路径加载应用程序特定的类,可以使用-cp或-classpath命令来在程序执行时动态设置。它在内部使用映射到java.class.path的环境变量。该类加载器由sun.misc.Launcher$AppClassLoader类用Java实现。
58+
- 无法卸载原则
5959

60-
除了上面讨论的3个主要的类加载器,程序员还可以直接在代码本身上创建用户定义的类加载器。这通过类加载器委托模型保证了应用程序的独立性。这种方法用于Tomcat之类的Web应用程序服务器中,以使Web应用程序和企业解决方案独立运行
60+
即使类加载器可以加载类,但是无法卸载已加载的类。替代卸载功能的是可以删除当前的类加载器,并创建一个新的类加载器
6161

62-
每个类加载器都有自己的名称空间来保存已加载的类。当类加载器加载类时,它将基于存储在名称空间中的完全合格的类名称(FQCN:Fully Qualified Class Name)搜索该类,以检查该类是否已被加载。即使该类具有相同的FQCN但具有不同的名称空间,也将其视为不同的类。不同的名称空间意味着该类已由另一个类加载器加载。
62+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/java_class_loaders.png)
6363

6464
- 链接
6565

66-
在遵循以下属性的同时,链接涉及验证和准备已加载的类或接口,其直接超类和超接口以及其元素类型
66+
在遵循以下属性的同时,链接涉及验证和准备已加载的类或接口,其直接父类和实现的接口以及其元素类型
6767

6868
- 在链接一个类或接口之前,必须将其完全加载。
6969
- 在初始化类或接口之前,必须对其进行完全验证和准备(在下一步中)。
@@ -148,9 +148,7 @@ Hotspot虚拟机的对象头包括两部分信息,第一部分用于存储对
148148

149149
然后,对于每个已加载的.class文件,它都会按照java.lang包中的定义,恰好创建一个Class对象来表示堆内存中的文件。稍后,在我们的代码中,可以使用此Class对象读取类级别的信息(类名称,父名称,方法,变量信息,静态变量等)。
150150

151-
152-
153-
TODO ... 具体可参考
151+
[具体可参考Java内存模型](https://github.com/CharonChui/AndroidNote/blob/master/JavaKnowledge/Java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.md)
154152

155153

156154

@@ -197,9 +195,7 @@ TODO ... 具体可参考
197195

198196
### Garbage Collector (GC)
199197

200-
只要引用了一个对象,JVM就会认为它是活动的。一旦不再引用对象,因此应用程序代码无法访问该对象,则垃圾收集器将其删除并回收未使用的内存。通常,垃圾回收是在后台进行的,但是我们可以通过调用System.gc()方法来触发垃圾回收(同样,无法保证执行。因此,请调用Thread.sleep(1000)并等待GC完成)。
201-
202-
198+
只要引用了一个对象,JVM就会认为它是活动的。一旦不再引用对象,因此应用程序代码无法访问该对象,则垃圾收集器将其删除并回收未使用的内存。通常,垃圾回收是在后台进行的,但是我们可以通过调用System.gc()方法来触发垃圾回收(同样,无法保证执行。因此,请调用Thread.sleep(1000)并等待GC完成)。[具体内存回收部分请看JVM垃圾回收机制](https://github.com/CharonChui/AndroidNote/blob/master/JavaKnowledge/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6.md)
203199

204200

205201

@@ -209,13 +205,27 @@ TODO ... 具体可参考
209205

210206
主应用程序线程是作为调用公共静态void main(String [])的一部分而创建的主线程,而所有其他应用程序线程都是由该主线程创建的。应用程序线程执行诸如执行以main()方法开头的指令,在Heap区域中创建对象(如果它在任何方法逻辑中找到新关键字)之类的任务等。
211207

212-
The major system threads are as follows.
208+
主要的系统线程有:
209+
210+
- Compiler threads
211+
212+
在运行时,这些线程将字节码编译为本地代码。
213+
214+
- GC threads
215+
216+
所有与GC相关的活动均由这些线程执行。
217+
218+
- Periodic task thread
219+
220+
计划周期性操作执行的计时器事件(即中断)由该线程执行。
221+
222+
- Signal dispatcher thread
223+
224+
该线程接收发送到JVM进程的信号,并通过调用适当的JVM方法在JVM内部对其进行处理。
225+
226+
- VM thread
213227

214-
- **Compiler threads**: At runtime, compilation of bytecode to native code is undertaken by these threads.
215-
- **GC threads**: All the GC related activities are carried out by these threads.
216-
- **Periodic task thread**: The timer events (i.e. interrupts) to schedule execution of periodic operations are performed by this thread.
217-
- **Signal dispatcher thread**: This thread receives signals sent to the JVM process and handle them inside the JVM by calling the appropriate JVM methods.
218-
- **VM thread**: As a pre-condition, some operations need the JVM to arrive at a safe point where modifications to the Heap area does no longer happen. Examples for such scenarios are “stop-the-world” garbage collections, thread stack dumps, thread suspension and biased locking revocation. These operations can be performed on a special thread called VM thread.
228+
前提条件是,某些操作需要JVM到达安全点才能执行,在该点不再进行对Heap区域的修改。这种情况的示例是“世界停止”垃圾回收,线程堆栈转储,线程挂起和有偏向的锁吊销。这些操作可以在称为VM线程的特殊线程上执行。
219229

220230

221231

0 commit comments

Comments
 (0)