JVM: Java内存结构

JVM

1. 基本概念

jvm是可运行Java代码的虚拟计算机.使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码,ByteCode),就可以在多种平台上不加修改地运行.

2. 运行过程

我们都知道Java源文件,通过编译器,能够生成相应的.class文件(字节码),而字节码文件又通过Java虚拟机中的解释器,编译成特定机器的机器码.

   1. Java源文件----jvm编译器-->字节码文件
   2. 字节码文件---->jvm解释器-->机器码

3. Jvm跨平台

Java语言为何跨平台? https://www.cnblogs.com/028fly/archive/2010/04/04/1704248.html

Java源码编译之后的代码是字节码文件,然后不同的硬件平台上安装有不同的JVM,由JVM把字节码再"翻译"成对应的硬件平台的机械码.由此实现了Java的跨平台

4. Jvm内存模型

https://www.cnblogs.com/21-forever/p/10951496.html
在这里插入图片描述

程序计数器

转载:https://blog.csdn.net/leaf_0303/article/details/78953669

当前线程正在执行或者下一条JVM指令码

程序计数器是一块较小的内存空间,程序计数器会随着线程的启动而创建,它的作用可以看作是当前线程所执行的字节码的行号指示器

======================================================================================================

在这里插入图片描述

虚拟机栈

https://www.cnblogs.com/manayi/p/9293302.html

虚拟机栈是用于描述java方法执行的内存模型
每个java方法在执行时,会创建一个“栈帧(stack frame)”,栈帧的结构分为“局部变量表、操作数栈、动态链接、方法出口”几个部分(栈帧是一个方法执行时所需要数据的结构)

我们常说的“堆内存、栈内存”中的“栈内存”指的便是虚拟机栈,确切地说,指的是虚拟机栈的栈帧中的局部变量表,因为这里存放了一个方法的所有局部变量

每个线程run时都会分配自己的栈内存空间,每个方法运行时会在自己的栈内存中分配栈帧内存区,各方法的局部变量存储于各自的栈帧内存区

每个线程包含一个栈区,栈中只保存基本数据类型和对象的引用
在这里插入图片描述
方法调用直至结束,就是栈帧入栈到出栈的过程
栈帧随着方法的调用而创建,随着方法结束而销毁

class{
	private int i;
}

如上面的全局变量i,他是存放在java堆中。因为它不是静态的变量,不会独立于类的实例而存在,而该类实例化之后,放在堆中,当然也包含了它的属性i

本地方法栈

https://www.jianshu.com/p/8a775d747c47

它与Java虚拟机栈所发挥的作用是类似的,其区别在于虚拟机栈为虚拟机执行Java方法(字节码)服务,
而本地方法栈则是为虚拟机使用到的 Native(非Java) 方法服务.

  本地方法:简单地讲,一个Native Method就是一个java调用非java代码的接口

Navtive 方法是 Java 通过 JNI 直接调用本地 C/C++ 库,可以认为是 Native 方法相当于 C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法

在这里插入图片描述当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法

本地方法栈是一个先进后出(后进先出)栈 由于是线程私有的,声明周期随着线程,线程启动而产生,线程结束而死亡 本地方法栈会抛出
StackOverflowError(堆栈溢出) 和 OutOfMemoryError(内存溢出) 异常

jvm内存
  JVM内存划分为堆内存和非堆内存(永久代)

转载https://blog.csdn.net/lingbo229/article/details/82586822



堆内存用途:存放对象和数组对象,垃圾收集器就是收集这些对象,然后根据GC算法回收

Jvm只有一个堆,它被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

堆从GC的角度还可以分为新生代和老年代
在这里插入图片描述



永久代(方法区)

永久代,也称为方法区,指内存的永久保存区域,主要存储运行时常量池、静态变量、类信息,类在被加载的时候被放入永久代,它和堆不同,GC不会在主程序运行期对永久代进行清理.所以可能造成OOM异常

JDK1.8废除了永久代用元空间代替,元空间与永久代类似,最大区别是:元空间并不在JVM中,而是使用本地内存

为什么要用Metaspace替代方法区
  随着动态类加载的情况越来越多,这块内存变得不太可控,如果设置小了,系统运行过程中就容易出现内存溢出,设置大了又浪费内存

运行时常量池是方法区的一部分

首先我们要搞清楚什么是常量池,有那些常量池
class常量池:.class文件中的class常量池,用于存放编译期产生的各种字面量(常量)和符号引用,当类加载到内存中后,Jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个

字符串常量池:字符串常量池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中



5.Jvm类加载机制(类的生命周期)

转载:https://www.cnblogs.com/Cubemen/p/10913633.html

Java程序实际上是将.class文件放入JVM中运行。虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换,解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是JVM的类加载机制

JVM类加载机制分为五个部分:加载, 验证, 装备, 解析, 初始化
在这里插入图片描述

  • 加载: 要在内存中生成一个代表这个类java.lang.class对象,类被加载时放入方法区

  • 验证: 加载阶段未完成,验证阶段已经开始.两者之间会交叉运行,为了保护虚拟机,JVM会有以下四个方面的验证
      文件格式验证
      元数据验证
      字节码验证
      符号引用验证

  • 准备: 为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

这个时候进行内存分配的只包括类变量(被static修饰的变量),并不包括实例变量,实例变量是在对象实例化时随对象一起分配在java堆中

  • 解析: 将符号引用转换为直接引用的过程
  • 初始化: 此阶段,才真正开始执行类中定义的Java程序代码

6.Jvm类加载器

在这里插入图片描述

  • 启动类加载器
       负责加载JAVA_HOME\lib目录下的类库到虚拟机内存中

  • 扩展类加载器
       负责加载JAVA_HOME\lib\ext目录下的的类库,用来加载java的扩展库,开发者可以直接使用这个类加载器

  • 应用程序类加载器
      这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载.一般情况下这就是系统默认的类加载器

  • 自定义类加载器
       这个加载器可以满足我们加载类的特殊需求,需要继承java.lang.ClassLoader类并且覆盖其中的findClass()方法和definedClass()方法

什么是双亲委派机制?
在这里插入图片描述

  双亲委派模型是一种组织类加载器之间关系的一种规范,它的工作原理是:如果一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,这样层层递进,最终所有的加载请求都被传到最顶层的启动类加载器中,只有当父类加载器无法完成这个加载请求(它的搜索范围内没有找到所需的类)时,才会交给子类加载器去尝试加载

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值