java提升:JVM运行时的区域以及拓展

jvm官方文档

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html

运行时数据区

数据区的描述

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.

Java虚拟机定义了在程序执行期间使用的各种运行时数据区域。其中一些数据区域是在Java虚拟机启动时创建的,只有在Java虚拟机退出时才会销毁。其他数据区域是每个线程。每个线程的数据区域在线程创建时创建,在线程退出时销毁。

总结:

1.jvm创建的时候,退出的时候销毁

2.每个Thread独有,Thread创建时常见,Thread退出时销毁。

==>有一些区是共享的,有一些区是线程自己的 独享的

运行时数据区

jvm运行时的数据区有一下6个

  • The pc Register

  • Java Virtual Machine Stacks
  • Heap
  • Method Area
  • Run-Time Constant Pool
  • Native Method Stacks

还有一个非jvm的: 堆外内存

如果调用底层源码的时候,可以使用堆外内存。但是不调用底层源码的时候,尽量不要使用堆外内存。

1.The pc Register 程序计数器

JVM 能够支持多线程执行。每一个JVM线程都有自己独有的pc register。任何时间节点,每个JVM都都在每一个执行执行的代码上。

占用一小块的内存,计数器为 当前线程锁执行的字节码的行号指示器。

记录当前线程目前执行到代码所对应的的哪一条字节码指令。

比如在多线程方式运行程序时,会多个线程并发处理,每个线程都有自己的程序计数器来标记自己的线程执行到哪里。

假如A线程在运行,B运行拿到的执行权非常高,这个时候A会被停止下来。等到B运行完成后,A会抢占资源继续执行,这个时候就需要A线程执行时的字节码。

那么什么是字节码呢?

通过不同的字节码的指令,来让应用程序清楚程序应该做什么事情,aload_0 就是字节码指令

(1)写一段最简单的java代码:HelloWord.java

public class HelloWord {

    public static void main(String[] args) {
        System.out.println("Hello Word!");
    }
}

(2)HelloWord.java只是我们自己开发完成的java源代码,这个时候需要使用将代码编译成字节码class文件。

javac HelloWord.java

(3) 使用javap将class文件写入到text文本中

javap -verbose HelloWord.class >> HelloWord.txt

HelloWord.txt的内容如下

Classfile /Users/wangyichao/Desktop/HelloWord.class
  Last modified 2019-10-26; size 423 bytes
  MD5 checksum 7c22fac0e6a2b992b4e072a2e28ef0df
  Compiled from "HelloWord.java"
public class HelloWord
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // Hello Word!
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // HelloWord
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               HelloWord.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               Hello Word!
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               HelloWord
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public HelloWord();
    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 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello Word!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 4: 0
        line 5: 8
}
SourceFile: "HelloWord.java"

2.Java Virtual Machine Stacks JVM栈

每个Java虚拟机线程都有一个私有的Java虚拟机堆栈,与线程同时创建。JVM里会存储frames(栈帧) 什么是frames在后面会进行讲解。

如果计算的线程需要的大小比JVM栈大,就会抛出StackOverflowError的错误(比如方法掉方法本身死循环)

调动方法时,会为每个方法创建frame 入栈,当方法执行完毕之后,会执行出栈。

非native或非java本身的的会放在jvm栈中,和 Native Method Stacks 对比看一下。

3.Heap

JVM有一个堆,堆是JVM线程共享的一个区域。堆是为所有类实例和数组分配内存的运行时数据区域。

堆会在虚拟机创建的开启的时候被创建。堆会存储一些对象,当然这个对象会被一个 自动的存储管理系统( garbage collector) 释放。

如果一个计算需要非常多个Heap,比整个我们能用的存储系统还要大的时候,这个时候会抛出OutOfMemoryError(比如不停的实例化对象或者一个非常大的数组)

延伸

但是存在一个问题就是,当JVM栈中的一些方法使用完成出栈之后,堆中分配的内存就是无实在意义了。这个时候就要GC来讲堆中的数据进行回收。这个就涉及到内存回收的东西。

4.Method Area 在1.8以后叫Metaspace

jvm里有一个方法区,这个方法区也是JVM线程共享的。

方法区在vm创建的时候创建。

方法区存储pool, field and method data, and the code for methods and constructors

说白了就是class文件加载到方法区。

如果方法区域中的内存不能满足分配请求OutOfMemoryError

 

5.Run-Time Constant Pool

每个类或每个接口的运行时都会有常量池,这个常量池其实就是存放在Method Area。

在创建类或接口时,如果构建运行时常量池所需的内存超过了Java虚拟机的方法区域所能提供的内存,就会抛出 OutOfMemoryError的异常

6.Native Method Stacks

当JVM使用JAVA以外的编写的方法(例如C堆栈)或者是native修饰的方法时,会放在native的方法栈中。

如果线程中的计算需要比允许的更大的本机方法堆栈,会抛出StackOverflowError异常

如果可以动态扩展本机方法堆栈,并且尝试扩展本机方法堆栈,但是可用的内存不足,或者如果可用的内存不足,无法为新线程创建初始本机方法堆栈,会抛出OutOfMemoryError异常

native修饰的,就会存放在native的方法栈中

非native修饰的方法 是调用本地操作系统里面的方法

JVM图解

 

Frames

frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.

Frame通常是用来存储数据和部分结果。

局部变量等就会存放在fram里。

当一个方法被调用的时候,一个Frame就会被创建。当方法被执行完成的时候就会销毁掉frame。

例如下面这个代码:

当实例化 hello这个方法的时候,frame就会被创建,当执行完 hello.hello后,会销毁掉frame

public static void main(String[] args) {

    HelloWord hello = new HelloWord();
    hello.hello();
}

public void hello() {
    String name = "java";   //局部变量,存放在 Java Virtual Machine Stacks

    System.out.println("Hello:" + name);
}

案例分析

1.分析程序中的输出结果

String s1 = new String("TestString"); //产生了两个个对象:1.String()存在heap里 2.方法区里面存储常量 s1,c常量值为ruozedata
String s2 = "TestString";

System.out.println(s1 == s2);//结果为false,比较的是引用的值,他们的地址是不一样的
System.out.println(s1.equals(s2));//结果为true,equals真实的值,和hashcode有关
s1 s1是一个变量,是对象的引用,它产生了两个对象 1.String()存在heap里 2.方法区里面存储常量 s1,c常量值为ruozedata

 是常量,因为Method Area已经有了值,s2不会产生对象

2.分析下面程序的输出结果

String s1 = "我爱";
String s2 = "大数据";
String s3 = "我爱" + "大数据";
String s4 = s1 + s2;
String s5 = "我爱大数据";
String s6 = s4.intern();

System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//true
System.out.println(s4 == s5);//false
System.out.println(s3 == s6);//true

结果分析

3.分析结果

Integer i1 = 50;
Integer i2 = 50;

Integer i3 = 150;
Integer i4 = 150;

Double d1 = 10.1D;
Double d2 = 10.1D;

System.out.println(i1 == i2);//true
System.out.println(i3 == i4);//false
System.out.println(d1 == d2);//false

为什么i1 == i2 是true,i3 == i4 是false

基本类型:int long

包装类型:Integer String

包装类型会有一个实例化的过程

我们看Interger.java的源码和Double.java的源码

Integer.java

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

在源码中我们可以看到 [-128,127]在java的pool中,在这个范围内的值,都是true。超出这个范围,会新创建一个对象,实例化的东西都会在Heap中,所以超出这个范围内的值 用 ==肯定是false的。

Double.java

 

public static Double valueOf(String s) throws NumberFormatException {
    return new Double(parseDouble(s));
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值