JVM运行时数据区

整个JVM构成里面,由三部分组成:类加载系统、运行时数据区、执行引擎

在这里插入图片描述
在这里插入图片描述

Java堆在JVM启动时创建内存区域去实现对象、数组与运行时常量的内存分配,它是虚拟机管理最大的,也是垃圾回收的主要内存区域 。
内存划分:
核心逻辑就是三大假说,基于程序运行情况进行不断的优化设计。
在这里插入图片描述

堆内存为什么会存在新生代和老年代?

分代收集理论:当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法 则,它建立在两个分代假说之上:

  • 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的。
  • 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消 亡。

这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则:收集器应该将Java堆划分出不同 的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
如果一个区域中大多数对象都是朝生夕灭,难以熬过垃圾收集过程的话,那么把它们集中放在一 起,每次回收时只关注如何保留少量存活而不是去标记那些大量将要被回收的对象,就能以较低代 价回收到大量的空间; 如果剩下的都是难以消亡的对象,那把它们集中放在一块,虚拟机便可以使用较低的频率来回收这 个区域。
这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用。

为什么新生代里面需要有两个Survivor区域呢?

下文讲

虚拟机栈

1)栈帧是什么?

栈帧(Stack Frame)是用于支持虚拟机进行方法执行的数据结构栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程。
栈内存为线程私有的空间,每个线程都会创建私有的栈内存,生命周期与线程相同,每个Java方法在执 行的时候都会创建一个栈帧(Stack Frame)。栈内存大小决定了方法调用的深度,栈内存过小则会导 致方法调用的深度较小,如递归调用的次数较少。

2)当前栈帧

一个线程中方法的调用链可能会很长,所以会有很多栈帧。只有位于JVM虚拟机栈栈顶的元素才是有效的,即称为当前栈帧,与这个栈帧相关连的方法称为当前方法,定义这个方法的类叫做当前类。 执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。

3)什么时候创建栈帧

调用新的方法时,新的栈帧也会随之创建。并且随着程序控制权转移到新方法,新的栈帧成为了当前栈 帧。方法返回之际,原栈帧会返回方法的执行结果给之前的栈帧(返回给方法调用者),随后虚拟机将会丢 弃此栈帧。

4)栈异常的两种情况

  • 如果线程请求的栈深度大于虚拟机所允许的深度(Xss默认1m),会抛出StackOverflowError异常
  • 如果在创建新的线程时,没有足够的内存去创建对应的虚拟机栈,会抛出OutOfMemoryError异常 【不一定】

本地方法栈

本地方法栈和虚拟机栈相似,区别就是虚拟机栈为虚拟机执行Java服务(字节码服务),而本地方法栈
为虚拟机使用到的Native方法(比如C++方法)服务。 简单地讲,一个Native Method就是一个Java调用非Java代码的接口。

public class IHaveNatives
   {
   
     native public void Native1( int x ) ;
     native static public long Native2() ;
     native synchronized private float Native3( Object o ) ;
     native void Native4( int[] ary ) throws Exception ;
}

为什么需要本地方法?

Java是一门高级语言,我们不直接与操作系统资源、系统硬件打交道。如果想要直接与操作系统与硬件 打交道,就需要使用到本地方法了。说白了,Java可以直接通过native方法调用cpp编写的接口!多线程 底层就是这么实现的,在多线程部分我们会看一下Thread实现的源码,到时候就可以理解了。

方法区

方法区(Method Area)是可供各个线程共享的运行时内存区域,方法区本质上是Java语言编译后代码 存储区域,它存储每一个类的结构信息,例如:运行时常量池、成员变量、方法数据、构造方法和普通 方法的字节码指令等内容。很多语言都有类似区域。

方法区的具体实现有两种:永久代(PermGen)、元空间(Metaspace)

1)方法区存储什么数据?

在这里插入图片描述
如果需要访问方法区中类的其他信息,都必须先获得Class对象,才能取访问该Class对象关联的方法信 息或者字段信息。

2)永久代和元空间的区别是什么?

  1. JDK1.8之前使用的方法区实现是永久代,JDK1.8及以后使用的方法区实现是元空间。
  2. 存储位置不同:
    永久代所使用的内存区域是JVM进程所使用的区域,它的大小受整个JVM的大小所限制。 元空间所使用的内存区域是物理内存区域。那么元空间的使用大小只会受物理内存大小的限 制。
  3. 存储内容不同:
    永久代存储的信息基本上就是上面方法区存储内容中的数据元空间只存储类的元信息,而静态变量和运行时常量池都挪到堆中

3)为什么要使用元空间来替换永久代?

  1. 字符串存在永久代中,容易出现性能问题和永久代内存溢出。
  2. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
  3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  4. Oracle 计划将HotSpot 与 JRockit 合二为一。

程序计数器

程序计数器(Program Counter Register),也叫PC寄存器,是一块较小的内存空间,它可以看作是当 前线程所执行的字节码指令的行号指示器。字节码解释器的工作就是通过改变这个计数器的值来选取下 一条需要执行的字节码指令。分支,循环,跳转,异常处理,线程回复等都需要依赖这个计数器来完 成。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-21 21:54:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-21 21:54:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-21 21:54:01       82 阅读
  4. Python语言-面向对象

    2024-02-21 21:54:01       91 阅读

热门阅读

  1. vim 寄存器

    2024-02-21 21:54:01       51 阅读
  2. 算法日记-02完全背包和多重背包问题总结

    2024-02-21 21:54:01       42 阅读
  3. 2. C++ 线程的使用

    2024-02-21 21:54:01       44 阅读
  4. 设计模式(六):模板方法模式(行为型模式)

    2024-02-21 21:54:01       51 阅读
  5. Scrapy里面的Xpath解析器问题

    2024-02-21 21:54:01       48 阅读
  6. 手机NFC录入门禁数据,实现手机开门

    2024-02-21 21:54:01       49 阅读
  7. STL简介

    2024-02-21 21:54:01       46 阅读
  8. spark-sql

    2024-02-21 21:54:01       41 阅读