V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
a1127889067
V2EX  ›  Java

Java 虚拟机系列之 Java 内存结构简介

  •  
  •   a1127889067 · 2019-03-01 20:44:54 +08:00 · 1037 次点击
    这是一个创建于 1854 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文我们将讲解 Java 虚拟机中各个区域以及各个区域的作用。 如果本文有什么疑问或者是错误,欢迎指出留言,互相学习以后会长期更新干货

    一.程序计数器 什么是程序计数器,有什么作用? 程序技术器是一块比较小的内存区域,主要当做是线程中所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一个执行的字节码命令,分支、循环、跳转等基础功能都是依赖这个程序计数器来完成。

    有什么特点?

    Java 虚拟机中的多线程是通过线程轮流切换分配处理器执行时间的方式来实现的,所以为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,所以是线程私有的。

    注意:

    如果线程执行的是 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值为空,此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemory 的情况的区域

    二.Java 虚拟机栈

    Java 虚拟机栈是干嘛的?

    Java 虚拟机栈描述的是 Java 内存模型,每个方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法返回地址等信息。每一个方法从调用到执行结束对应着一个栈帧在虚拟机栈中入栈出栈的过程。

    有什么特点?

    Java 虚拟机栈是线程私有的,而且生命周期和线程相同。

    注意:

    如果线程请求的栈的深度大于虚拟机所允许的深度,将抛出 StackOverFlowError 异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够多的内存,就会抛出 OutOfMemoryError 异常。

    三.本地方法栈

    与虚拟机栈发挥的功能类似,区别不过是虚拟机栈为虚拟机执行 Java 方法,本地方法栈为虚拟机用到的 Native 方法服务。本地方法栈也会抛出 StackOverFlowError 和 OutOfMemoryError 异常。

    四.Java 堆

    什么是 Java 堆,有什么作用?

    Java 堆是 Java 虚拟机中所管理内存中最大的,是所有线程共享的一块内存区域,在虚拟机启动的时候创建,目的是存放对象实例。

    有什么特点?

    几乎所有的对象实例都在这里分配内存,因此这也是垃圾收集器管理最主要的区域,而且 Java 堆可以处于物理不连续的内存空间,只要逻辑连续即可

    1、Java 堆中还细分新生代和老年代,然后新生代中又分为 Eden 区域、From Survivor 空间、To Survivor 空间等。

    2、这里的 Java 堆的垃圾回收算法是使用了分代收集算法,即新生代中使用“复制”算法,而老年代中使用“标记-清理”算法或“标记-整理”算法。

    注意:

    当前主流虚拟机都是可扩展来实现的(通过-Xms 和-Xmx 来控制),如果堆中没有内存完成实例,并且堆也无法再扩展时,会抛出 OutOfMemoryError 异常。

    五.方法区

    方法区的作用是什么?

    方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。

    为啥说方法区不等价于永久区?

    方法区不等价于永久代,只是 HotSpot 虚拟机的设计团队选择把 GC 分代收集扩展至方法区,是为了 HotSpot 垃圾收集器可以像管理 Java 堆一样管理这部分内存,能够省去专门为方法区编写内存管理代码的工作,但是这样也会更容易造成内存溢出的问题(永久代可以用-XX:MaxPermSize 设置上限),所以在 JDK1.7 的 HotSpot 已经把永久代的字符串常量池移出,移到了 Java Heap 区,可在 《深入解析 String#intern 》 了解

    运行时常量池有什么作用?

    运行时常量池也是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分将在类加载后进入方法区的运行时存放。

    运行时常量池的特点?

    Java 虚拟机对 Class 文件每一部分的格式都有严格规定,每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行,然而对于运行时常量池却没做任何要求 运行时常量具有动态性,可以在运行期间将新的常量放入池中,比如 String 的 intern()方法

    六.直接内存

    直接内存是 Java 虚拟机的内存吗?

    直接内存并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范定义的内存区域,是使用本机的内存,又名堆外内存。

    怎么在 Java 中使用堆外内存?

    在 JDK1.4 中的 NIO 类,引入了基于通道( Channel )与缓冲区( Buffer )的 I/O 方式,可以使用 Native 函数库来直接分配堆外内存,然后在 Java 堆中使用 DirectByteBuffer 对象来作为这块内存的引用进行操作。

    注意:

    虽然堆外内存不受 Java 堆大小的管理,但是受本机总内存以及处理器寻址空间的限制。在服务器管理员配置虚拟机参数的时候,可以通过-Xmx 等来配置 Java 虚拟机最大内存,但经常忽略直接内存,导致各个内存区域总和大于物 理内存限制,导致 OutOfMemoryError 异常。

    如果本文有什么疑问或者是错误,欢迎指出留言,互相学习同时希望大家可以加一下小编的群:790147974 以后会长期更新干货

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5883 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 02:21 · PVG 10:21 · LAX 19:21 · JFK 22:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.