Java 基础:JVM虚拟机结构

荒凉一梦 提交于 2019-12-11 23:06:57

一、JVM结构

1.Class Loader:依据特定格式,加载class文件到内存
2.Execution Engine:对加载的二进制字节码命令进行解析
3.Native Interface:融合不同开发语言的原生库为Java所用
4.Runtime Data Area:JVM内存空间结构模型

二、JVM内存空间结构模型

1.内存模型结构

 
(1)    线程私有
程序计数器(字节码指令)、虚拟机栈(java方法)、本地方法栈(native方法)
(2)    线程共享
MetaSpace、Java堆


2. 线程私有:程序计数器(Program Counter Register)

作用:
(1)线程独立拥有计数器,是当前线程所执行的字节码行号指示器(逻辑)
(2)改变计数器的值来选取当前线程需要执行的下一条字节码指令
(3)线程独有,即一个线程拥有一个计数器
(4)对Java方法计数,如果是Native方法则计数器值为Undefined
(5)不会发生内存泄漏

3. 线程私有:Java虚拟机栈

(1)作用:java方法执行的内存模型,包含多个栈帧
(2)程序执行过程:对于当前线程,每个方法会产生1个栈帧,当方法执行结束后,该栈帧取消。而每个栈帧中包含:局部变量表、操作栈、动态链接、返回地址等等。
 
 
(3)    当前栈中包含内容
局部变量表:包含了执行过程中的所有变量(boolen,char,string等等)
操作数栈:操作数栈:入栈、出栈、复制、交换、产生消费变量(4)    实例实现当前线程调用过程
测试类:MemoryCodeSample.java

package com.spring.ioc.c5;

public class MemoryCodeSample {

    public static int add(int a,int b){
        int c=0;
        c=a+b;
        return c;
    }
}
  • 编译:

src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class

  • 反编译:

src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class

  • 结果:

Classfile …/src/main/java/com/spring/ioc/c5/MemoryCodeSample.class
  Last modified 2019-12-11; size 292 bytes
  MD5 checksum 6d23ead3e45557a26587f2d70fbb9ee5
  Compiled from "MemoryCodeSample.java"
public class com.spring.ioc.c5.MemoryCodeSample            //描述类信息
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#12         // java/lang/Object."<init>":()V
   #2 = Class              #13            // com/spring/ioc/c5/MemoryCodeSample
   #3 = Class              #14            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               add
   #9 = Utf8               (II)I
  #10 = Utf8               SourceFile
  #11 = Utf8               MemoryCodeSample.java
  #12 = NameAndType        #4:#5          // "<init>":()V
  #13 = Utf8               com/spring/ioc/c5/MemoryCodeSample
  #14 = Utf8               java/lang/Object
{
  public com.spring.ioc.c5.MemoryCodeSample();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public static int add(int, int);
    descriptor: (II)I                            // 接受两个int输入,返回也是一个int
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=2            // 栈容量2,本地变量3,参数2个
         0: iconst_0                        // 把0压入操作数栈顶;同时获取输入的局部变量(1,2)
         1: istore_2                        // 把操作数栈顶元素取出,放入局部变量temp[2]位置
         2: iload_0                        // 把局部变量temp[0]压入操作数栈顶
         3: iload_1                        // 把局部变量temp[1]压入操作数栈顶
         4: iadd                            // 把操作数栈中元素加和,压入操作数栈顶
         5: istore_2                        // 把操作数栈顶元素取出,放入局部变量temp[2]
         6: iload_2                        // 把局部变量temp[2]压入操作数栈中
         7: ireturn                        // 把操作数栈顶元素返回
      LineNumberTable:
        line 6: 0                                //代码第6行,对应字节码第0行
        line 7: 2                                //代码第7行,对应字节码第2行
        line 8: 6                                //代码第8行,对应字节码第6行
}
SourceFile: "MemoryCodeSample.java"

具体过程add(1+2)实例

4. 线程私有:本地方法栈

与虚拟机栈类似,主要用于标注native方法

5.线程共享:元空间(MetaSpace)

(1)    作用:存储class基本信息,包括java对象的method和field等
(2)    元空间使用的是本地内存,而永久代是使用jvm内存
(3)    元空间(MetaSpace)与永久代(PermGen)区别
在Java8中,元空间(Metaspace)登上舞台,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。
 
参考:https://www.cnblogs.com/secbro/p/11718987.html


6.线程共享:java堆

(1)堆内存分类:根据对象存活的周期不同,把堆内存划分为:新生代、老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略。
(2)作用:堆内存是虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,我们程序所有的对象实例都存放在堆内存中。
给堆内存分代是为了提高对象内存分配和垃圾回收的效率。试想一下,如果堆内存没有区域划分,所有的新创建的对象和生命周期很长的对象放在一起,随着程序的执行,堆内存需要频繁进行垃圾收集,而每次回收都要遍历所有的对象,遍历这些对象所花费的时间代价是巨大的,会严重影响我们的GC效率。
有了内存分代,新创建的对象会在新生代中分配内存;经过多次回收仍然存活下来的对象存放在老年代中;静态属性、类信息等存放在永久代中。新生代中的对象存活时间短,只需要在新生代区域中频繁进行GC;老年代中对象生命周期长,内存回收的频率相对较低,不需要频繁进行回收;永久代中回收效果太差,一般不进行垃圾回收。还可以根据不同年代的特点采用合适的垃圾收集算法。分代收集大大提升了收集效率,这些都是内存分代带来的好处。

(2)Java堆的分配区域

  • JAVA1.8之前

 
参考:https://www.cnblogs.com/guanghe/p/10524314.html

  • JAVA1.8之后

参考:https://blog.csdn.net/ztx114/article/details/79400789


四、问题

1.递归由于调度栈过深,导致超过虚拟机栈帧深度会引发java.lang.StackOverflowError异常

(1)代码:斐波那契数列

package com.spring.ioc.c5;

public class Fibonacci {

    public static int  fibonacci(int n){
        if(n==0) return 0;
        if(n==1) return 1;
        return fibonacci(n-1)+fibonacci(n-2);
    }

    public static void main(String[] args) {
        System.out.println(fibonacci(1));
        System.out.println(fibonacci(2));
        System.out.println(fibonacci(3));
        System.out.println(fibonacci(4));
        System.out.println(fibonacci(5));

        System.out.println(fibonacci(1000000));
    }
}

报错:
1
1
2
3
5
Exception in thread "main" java.lang.StackOverflowError

(2)解决:限制递归次数,或者使用循环替换递归

2.内存不够导致java.lang.OutOfMemoryError问题

(1)代码

package com.spring.ioc.c5;

public class stackLeak {
    public void stackLeakByThread(){
        while (true){
            new Thread(){
                public void run(){
                    while (true){

                    }
                }
            }.start();
        }
    }
}

(2)报错:(警告:绝对不建议尝试,可能引起死机)
Exception in thread "main"java.lang OutOfMemoryError: unable to create new native thread


 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!