JVM的东西太多了,我们刚开始学java的时候,就会接触堆、栈,还有方法区,因为我们要知道new出来的对象放在哪里,局部变量放在哪里,static修饰的变量放在哪里。

我从网上截一个图:

这里有三大部分:

  • classloader
  • runtime data area
  • execution engine

classloader就是类加载器。比如说我这里有一个Main.java文件:

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<font style="color:rgb(77, 77, 77)"><font face="&quot;"><font style="font-size:16px">package com.ocean;
 
public class Main {
 
    public static final int intData = 100;
    public static User user = new User();
 
    public int compute(){
        int a = 1;
        int b = 2;
        int c = (a + b) * 3;
        return c;
    }
 
    public static void main(String[] args) {
            Main main = new Main();
            main.compute();
 
    }
}</font></font></font>

通过javac命令,将其编译成Main.class文件,然后classloader就会加载它。

但是,在这里有三个classloader。

首先是Bootstrap ClassLoader,它load的是java核心包,像java.lang,java.net,java.util,java.io,java.sql包中的class文件。

然后是Extension ClassLoader,它load的是$JAVA_HOME/jre/lib/ext中的class文件,它下的就不是核心库了,其实就是额外的库。

最后是Application ClassLoader。它加载的是类路径下的class。

我们感受一下这三个classloader的存在:

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
    public void printClassLoaders() throws ClassNotFoundException {
 
        System.out.println("Classloader of this class:"
                + Main.class.getClassLoader());
 
        System.out.println("Classloader of Logging:"
                + Logging.class.getClassLoader());
 
        System.out.println("Classloader of ArrayList:"
                + ArrayList.class.getClassLoader());
    }
 
    public static void main(String[] args) throws ClassNotFoundException {
            Main main = new Main();
//            main.compute();
        main.printClassLoaders();
         
    }

Arraylist是由bootstrap classloader加载的,但它是由native code(C和C++)写的,所以展现不出来(null)。

类的加载是用代理模式实现的。

比如JVM委托classloader instance把Main.class加载到内存,那么application classloader会委托父类加载器即extension classloader去加载,extension classloader会再向上委托由父类加载器bootstrap classloader去加载,只有当bootstrap classloader和extension classloader无法加载时,application classloader才会自己加载。

注意,classloader加载的时候会verify和prepare。

verify包括标记为final类型的类是否有子类,类中的final方法是否被子类进行重写,重写是否符合规范等等。

prepare包括为类中的静态变量分配内存空间,被final修饰的static变量直接赋值等。

anyway,Main.class文件被加载到了method area。

我们很早就知道,method area里面会存常量、静态变量和类信息,

我们这里的initData就会存在方法区:

public static final int intData = 100;

这个类信息,包括类的静态变量、初始化代码(静态变量的赋值和静态代码块)、实例变量、实例变量的初始化代码(构造方法)、实例方法、父类引用信息(super)。

今天主要讲的,就是runtime data area。

runtime data area里面的成分,像native method stacks,就没什么好讲的,每一条线程,都会有一个native method stacks,它也被叫做“C stacks”,就是用C语言写的方法。

好,我们进入正题。

我们进入main方法。

一个main方法,就是一条线程。

对于每条线程,都会有一个java virtual machine stack。

这里面,会有一个个stack frame,也就是说,每调用一个方法,就会有一个stack frame压进java virtual machine stack。当方法调用完毕,这个stack frame也就没了。所以这是一个first in last out的stack结构,main方法会最后一个结束,因为它是第一个被压进stack中的。

在每一个stack frame中,有local variable,就是局部变量,operand stack,操作数栈,还有dynamic linking,动态链接,以及method invocation completion,方法出口。

dynamic linking可以解释method override,还要结合 Run-Time Constant Pool来说,我们暂时不讲。

主要了解一下local variable与operand stack。

我们看一下用javap反编译后的jvm指令。

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Compiled from "Main.java"
public class com.ocean.Main {
  public static final int intData;
 
  public static com.ocean.User user;
 
  public com.ocean.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
 
  public int compute();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: iconst_3
       8: imul
       9: istore_3
      10: iload_3
      11: ireturn
 
  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/ocean/Main
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method compute:()I
      12: pop
      13: return
 
  static {};
    Code:
       0: new           #5                  // class com/ocean/User
       3: dup
       4: invokespecial #6                  // Method com/ocean/User."<init>":()V
       7: putstatic     #7                  // Field user:Lcom/ocean/User;
      10: return
}

看一下compute方法的执行过程:

iconst_1—>Push the int constant (-1, 0, 1, 2, 3, 4 or 5) onto the operand stack.

把int常量压入操作数栈,这里,值就是1。

istore_1—>Store int into local variable.The “n” must be an index into the local variable array of the current frame . The value on the top of the operand stack must be of type int. It is popped from the operand stack, and the value of the local variable at is set to value.

这个含义就是,把1这个值赋值给local variable a。

2: iconst_2

3: istore_2

这两句和上面一样。

iload_1—>Load int from local variable

加载int值。也就是说, local variable array 中下标为1的值(我们这里也正好是1,顺便说一下,这个array中,下标从0开始,0为this)被推到操作数栈栈顶。

iadd—>Both value1 and value2 must be of type int. The values are popped from the operand stack. The int result is value1 + value2. The result is pushed onto the operand stack.

1和2两个数从操作数栈栈顶弹出,然后相加的结果再压入栈顶。后面一样。最后

11: ireturn—>Return int from method。

返回int值。这里有几个东西。一个是程序计数器(pc register)。

每条线程都有一个pc register。

代码前面的01234就是程序计数器记录的值:

0: iconst_1

1: istore_1

2: iconst_2

3: istore_2

4: iload_1

因为多线程运行的时候,一个方法可能运行到一半,cpu时间片就被别的线程抢走了,到时候这个方法再次抢到cpu时间片时,从哪里再开始运行呢?程序计数器就会做好记录。

修改程序计数器的是执行引擎。

最后就是方法出口,compute方法调用完毕之后该回到哪里去呢?

[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
public static void main(String[] args) throws ClassNotFoundException {
1            Main main = new Main();
2            main.compute();
3        System.out.println("compute() is over!");
//        main.printClassLoaders();
         
    }

它肯定要回到main方法的第三行位置啊,这是由Normal Method Invocation Completion完成的。我们这里就不会抛什么异常了。

静态变量user存在方法区(存的是堆中User的地址),并且指向堆中的对象User。

堆里面,有young generation和old generation。

young generation包括eden space和survivor space。

survivor space有两个,一个是survivor1,另一个是survivor2。

old generation就是所谓的tenured space。

当我们创建了一个对象,JVM就把它放在eden space当中,伊甸园嘛,就是新生的对象。

eden space也有一个大小的,它满了之后,minor gc就会干活,它会把eden space当中的所有非垃圾对象挪到survivor space,那是survivor1还是survivor2,这不一定的。看名字也知道,这些就是还被引用指向的幸存对象。

一个对象从eden space挪到survivor space一次(比如说是s1),就可以认为年岁加一。

等到下一次eden space又满了,minor gc就会把eden space中的对象和s1中的对象挪到s2,这时最初在eden space中的对象年龄又加1,可以说是两岁了。

这些不死的对象就被minor gc这样在s1和s2之间挪来挪去,年龄不断增加,等到15岁时,就认为是老不死的对象了,并将其挪到老年代(tenured space)。

我们看一段程序:

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.ocean;
 
import java.util.ArrayList;
 
public class TestHeap {
 
    public static final String SUCCESS = "1";
 
    public static void main(String[] args) throws InterruptedException {
        ArrayList list = new ArrayList();
 
        while(true){
            list.add(new TestHeap());
            Thread.sleep(10);
        }
    }
}

然后在visual gc下的分析:

更多java学习资料可关注:itheimaGZ获取

JVM简述的更多相关文章

  1. JVM 简述

    JVM(Java Virtual Machine,Java虚拟机) Java程序的跨平台特性主要是指字节码文件可以在任何具有Java虚拟机的计算机或者电子设备上运行,Java虚拟机中的Java解释器负 ...

  2. JVM学习笔记——内存结构篇

    JVM学习笔记--内存结构篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存结构部分 我们会分为以下几部分进行介绍: JVM整体介绍 程序计数器 虚拟机栈 本地方法栈 堆 方法 ...

  3. JVM参数简述

    java虚拟机启动时会带有很多的启动参数,Java命令本身就是一个多参数的启动命令.那么具体JVM启动包含哪些参数呢?这篇文章针对java8的情况做一篇汇总解读,包含大多数常见和不常见的命令参数,过于 ...

  4. JVM内存回收机制简述

    JVM内存回收机制涉及的知识点太多了,了解越多越迷糊,汗一个,这里仅简单做个笔记,主要参考<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)> 目前java的jdk默认虚拟机为H ...

  5. JVM的粗略简述

    什么是Java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构,如处理器.堆栈.寄存器等,还具有相应的指令系统.JVM屏蔽了与 ...

  6. 03 JVM 从入门到实战 | 简述垃圾回收算法

    引言 之前我们学习了 JVM 基本介绍 以及 什么样的对象需要被 GC ,今天就来学习一下 JVM 在判断出一个对象需要被 GC 会采用何种方式进行 GC.在学习 JVM 如何进行垃圾回收方法时,发现 ...

  7. 简述 JVM 垃圾回收算法

    经典垃圾回收 标记-清除(Mark-Sweep) 研发园开了家新餐厅,餐厅老板在考虑如何回收餐盘时首先使用了最简单的方式,那就是服务员在顾客用餐的过程中,不定时的观察餐厅,针对用完餐的顾客记录他们的位 ...

  8. JVM运行时数据区内容简述

    JVM运行时数据区分为五个部分:程序计数器.虚拟机栈.本地方法栈.堆.方法区.如下图所示,五部分其中又分为线程共享区域和线程私有区域,下面将分别介绍每一部分. 1. PC程序计数器 程序计数器是一块较 ...

  9. JVM内存分配及GC简述

    在阐述JVM的内存区域之前,先来看下计算机的存储单位.从小到大依次为Bit,Byte,KB,MB,GB,TB.相邻的单位相差2的10次方. 计算机运行中的存储元件主要分为寄存器(位于CPU)和内存,寄 ...

随机推荐

  1. Java学习——代理模式

    Java中的三种代理模式 一,什么是代理模式? 代理模式是一种设计模式,简单的来说就是在不改变源码的情况下,实现对目标对象的功能扩展. 比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing ...

  2. linux-文件系统和目录

    linux目录说明 /:系统根目录 /usr:用户的程序 /home:默认创建用户在此目录下创建用户主目录 /etc:存放系统配置文件.服务脚本,一些程序配置文件 /bin:常用命令 /sbin:常用 ...

  3. 【Python】关于import QtCore报错的处理方法

    刚开始学习使用PyQT,但总碰到一些小挫折 比如 import Pyqt成功 而 from PyQt5 import QtCore, QtGui, QtWidgets却报错,找了半天终于找到资料,原因 ...

  4. UML-GoF设计模式-总结

    1.GRASP 2.设计模式

  5. HDU-2087 C - 剪花布条(KMP基本)

    http://acm.hdu.edu.cn/showproblem.php?pid=2087 一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能 ...

  6. 双向链表的双向冒泡排序 c++

    #include<iostream> using namespace std; #define swap(a,b) {int t;t = a;a = b;b = t;} //节点类型的定义 ...

  7. javaweb02

    第一个web服务器程序:开发部署到Tomcat服务器下运行 1).在eclipse新建一个Javaproject2).在java项目下创建web开发的目录结构 -Webcontent -WEB-INF ...

  8. 微信官方小程序示例demo 微信开发者工具打开不显示云开发按钮

    如果直接打开官方的demo,微信开发者工具上是不显示云开发按钮的. 是因为默认appid是测试号.要换成一个正式appid就会显示云开发按钮了. 分享一个朋友的人工智能教程.零基础!通俗易懂!风趣幽默 ...

  9. h5-sessionStorage储存的使用

    <!-- sessionStorage的使用:存储数据到本地.存储的容量5mb左右 1.这个数据本质是储存在当前页面的内存中 2.他的生命周期为关闭当前页面,关闭页面,数据会自动清楚 setTt ...

  10. 图论中最优树问题的LINGO求解

    树:连通且不含圈的无向图称为树.常用T表示.树中的边称为树枝,树中度为1的顶点称为树叶. 生成树:若T是包含图G的全部顶点的子图,它又是树,则称T是G的生成树. 最小生成树:设T=(V,E1)是赋权图 ...