JVM内存解析

JVM概念

1.JVM(Java Virtual Machine)
1)是Java平台的一部分,是一种能够运行Java bytecode的虚拟机
2)是硬件计算机的抽象(虚拟)实现,可以解释执行Java字节码
3)是实现Java跨平台运行的基石
2.jdk、jre、jvm是什么关系?
(1)JVM(Java Virtual Machine),==(Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
(2)JRE(Java Runtime Environment),
(Java运行环境,包含了JVM和Java的核查类库(Java API))也就是java平台。所有的java程序都要在JRE环境下才能运行。
(3)JDK(Java Development Kit),
(Java开发工具包,包含了JRE和开发工具)==是开发者用来编译、调试程序用的开发包。JDK也是JAVA程序需要在JRE上运行。
注:我们只需要安装jdk即可,jdk包含了Java运行环境和虚拟机
在这里插入图片描述

为什么要学习JVM

深入了解JVM可以帮助我们从平台角度提高解决问题的能力,例如:
1)有效防止内存泄漏(Memory leak)
2)优化线程锁的使用(Thread lock)
3)科学进行垃圾回收(throughput)
4)降低延迟(Delay).提高性能(performance)

JVM原理

(1)jvm是java的核心和基础,在java编译器和os平台之间的虚拟处理器,可在上面执行字节码程序。
(2)java编译器只要面向jvm,生成jvm能理解的字节码文件。java源文件经编译成字节码程序,通过jvm将每条指令翻译成不同的机器码,通过特定平台运行。
在这里插入图片描述

JVM执行程序的过程

1.加载.class文件
2.管理并分配内存
3.执行垃圾收集
JRE(java运行时环境)由JVM构造的java程序的运行环,也是Java程序运行的环境,但是他同时一个操作系统的一个应用程序一个进程,
因此他也有他自己的运行的生命周期,也有自己的代码和数据空间。
JVM在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,
提供一个完整的Java运行环境,因此也就虚拟计算机。

操作系统装入JVM是通过jdk中Java.exe来完成,
通过下面4步来完成JVM环境:
1.创建JVM装载环境和配置
2.装载JVM.dll
3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例
4.调用JNIEnv实例装载并处理class类。

JVM的生命周期

JVM实例对应了一个独立运行的java程序它是进程级别
1)启动:启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static voidmain(String[] args)函数的class都可以作为JVM实例运行的起点
2) 运行。main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以表明自己创建的线程是守护线程
3) 消亡。当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出
JVM执行引擎实例则对应了属于用户运行程序的线程它是线程级别的

JVM内存模型

在这里插入图片描述
注:总的来说就是,JDK1.7之前,运行时常量池(字符串常量池也在里边)是存放在方法区,此时方法区的实现是永久带。
JDK1.7字符串常量池被单独从方法区移到堆中,运行时常量池剩下的还在永久带(方法区)
JDK1.8,永久带更名为元空间(方法区的新的实现),但字符串常量池池还在堆中,运行时常量池在元空间(方法区)。

在这里插入图片描述

运行时数据区存储了哪些数据?

1)程序计数器(PC寄存器)
由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

2)java栈
Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)操作数栈(Operand Stack)指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。

3)本地方法栈
本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的

4)堆
Java中的堆是用来存储对象本身的以及数组(数组引用是存放在Java栈中的)。堆是被所有线程共享的,在JVM中只有一个堆。

5)方法区
与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息包括类的名称、方法信息、字段信息)、静态变量常量以及编译器编译后的代码等。
在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。
在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,
对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

JVM内存溢出的情况

在这里插入图片描述

1)程序计数器(Program Counter Register) 每条线程都有一个独立的的程序计数器,各线程间的计数器互不影响,因此该区域是线程私有的。该内存区域是唯一一个在Java虚拟机规范中没有规定任何OOM(内存溢出:OutOfMemoryError)情况的区域。

2)Java虚拟机栈(Java Virtual Machine Stacks) 在Java虚拟机规范中,对这个区域规定了两种异常情况:
1、如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
2、如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
这两种情况存在着一些互相重叠的地方:当栈空间无法继续分配时,到底是内存太小,还是已使用的栈空间太大,其本质上只是对同一件事情的两种描述而已。
在单线程的操作中,无论是由于栈帧太大,还是虚拟机栈空间太小,当栈空间无法分配时,虚拟机抛出的都是StackOverflowError异常,而不会得到OutOfMemoryError异常。
而在多线程环境下,则会抛出OutOfMemoryError异常。

3)堆Java Heap
Java Heap是Java虚拟机所管理的内存中最大的一块,它是所有线程共享的一块内存区域。几乎所有的对象实例和数组都在这类分配内存。Java Heap是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。
  根据Java虚拟机规范的规定,Java堆可以处在物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存可分配时,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。
4)方法区域,又被称为“永久代”,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。1.7之后没有永久代,只有元空间

案例测试:
package com.tedu.cn;

public class Main {
    public static void main(String[] args) {
        int a = 10;
        new Main().f1(a);
        System.out.println(a);

    }
    public void f1(int a){
        int b = 10;
        System.out.println(a+b);
        a=11;
    }
}

结果:形参改变,实参没有改变所以a=10,并且当

public void f1(int a){
        int b = 10;
        System.out.println(a+b);
        a=11;
    }

执行完之后会在栈中将ab赋完的值删除,最终a为10
,在这里插入图片描述

案例测试2
package com.tedu.cn;

public class Main {
    public static void main(String[] args) {
        Person p2 = new Person();
        p2.id=111;
        new Main().f1(p2);
        System.out.println(p2.id);

    }
    public void f1(Person p){
       p.id=222;
    }
}
class Person{
    int id;
    String name;
}

结果:String是对象存储在堆中,所以赋值会改变
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值