摘要

  • 自从java面世以来,声势浩大,提出“Write Once,Run Anywhere";Java相比于其余C/C++语言的劣势:在JVM内存治理之下,不再须要为每一个new操作去手动分配内存和free/delete的内存开释;不容易呈现内存透露和内存溢出等问题。
  • 本节次要解说Java运行时数据区:线程共享数据区:办法区、堆 线程隔离数据去:虚拟机栈、本地办法栈、程序计数器

思维导图

内容

1、运行时数据区蕴含哪几局部?


java虚拟机运行时数据区次要蕴含以下几个模块?

  • 线程共享数据区:办法区、堆
  • 线程隔离数据区:虚拟机栈、本地⽅法栈、堆、程序计数器

程序计数器:用来记录字节指令的行号;咱们将.java文件编译成.class文件后,交由JVM去执行的时候,程序一行一行执行就是交给程序计数器去做的
Java虚拟机栈:比方咱们写一个办法,JVM执行这个办法的时候,相似于创立了一个栈针;入栈到出栈就是这个办法调用的整个过程;对应的就是一个办法一个栈。
本地办法栈:就是JVM虚拟机执行一些本地办法库;咱们在进行一个CAS操作的时候:通过unsafe的compareAndSwapInt调到本地办法库外面的native办法。那么这些native办法就是在本地办法栈外面运行的。
办法区:寄存类信息,类变量,动态变量。
堆:简直所有数组跟对象的创立都是在堆外面。

2、程序计数器

是什么?

程序计数器(Program Counter Register)是一块较小的内存空间,是以后线程所执行的字节码行号指示器

为什么?

  1. 字节码解释器工作时候通过改变程序计数器的值来选取下一条要执行的字节码指令;线程的各个根底性能都须要依赖这个计数器来实现。
  2. java虚拟机多线程是通过线程轮流切换并调配处理器执行的工夫实现,在任意一个确定的时刻,一个处理器都只会执行一条线程指令,因而为了线程切换后能复原到正确的执行地位。每个线程都须要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储。咱们称这类内存区域为“线程公有”的内存。

特点?

内存区域中唯⼀⼀个在Java虚拟机标准中没有规定任OutOfMemoryError 状况的区域。因为程序计数器自身不须要咱们程序员去操作,所以不会呈现OOM。

实战演练

咱们创立一个Person类;领有属性age,提供getter/setter办法。

public class Person {    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    private int age;}

咱们应用javac进行编译源代码为字节码,而后应用javap查看字节码,如下:

咱们通过javap -l能够看到字节码的内容;咱们看到外面有两个办法:setAge;getAge;而后咱们看到getAge办法在第5行。setAge在8,9行。当初如果咱们须要执行Person外面的getAge办法;咱们说到,这个时候咱们可能多线程来执行这个办法;所以这块运行时数据库是一块独立的内存;咱们晓得程序计数器是一块线程隔离的数据库,所以每块线程有本人独立的程序计数器。这块内存是在咱们的线程外面独自隔离开来的,不同的线程保护了本人不同的程序计数器。

3、java虚拟机栈

上一节咱们解说了程序计数器;程序计数器是线程公有的一块小内存,线程公有的内存除了程序计数器之外还有两块:java虚拟机栈,本地办法栈。办法区跟堆绝对的就是线程共享的内存。

是什么?

Java办法执行的一块内存区域;随着线程办法执行时候压栈出栈,此内存区域也会销毁,他是跟线程的生命周期雷同的。

为什么?

办法的执行是依照栈数据结构;每个办法在执行的同时都会创立一个栈帧(Stack Frame)用于寄存局部变量表、操作数栈、动静链接、⽅法出⼝等信息。每⼀个⽅法从调⽤直⾄执⾏实现的过程,就对应着⼀个栈帧在虚拟机栈中⼊栈到出栈的过程。

特点?

  1. 局部变量表寄存了编译期可知(指代咱们这些根底数据类型他所对应的数据的大小,大小是可晓得的)的各种根本数据类型(boolean、byte、char、short、int、 float、long、double)以及对象引⽤(reference 类型)
  2. 如果线程申请的栈深度⼤于虚拟机所容许的深度,将抛出 StackOverflowError异样。

实战

public class StackDemo {    public static void a(){        System.out.println("method a executed");    }    public static void b(){        //a();        b();        System.out.println("method b executed");    }    public static void main(String[] args){       b();        System.out.println("method main executed");    }}

咱们批改b()的调用为本人。下面进行了递归调用:b()办法执行一直入栈操作,而没有出栈,导致所调配的栈内存不够,从而呈现栈内存溢出。栈长度超过制订长度大小。后果如下: