zoukankan      html  css  js  c++  java
  • 运行时数据区

    运行时数据区

    程序计数器

    也叫PC寄存器

    • 它是一块较小的内存空间;

    • 当前线程执行的字节码的行号指示器,是程序控制流的指示器;

    • 每个线程都有一个独立的程序计数器,线程间的计数器互不影响,独立存储,属于“线程私有”的内存;

    • 如果线程正在执行的是一个java方法,那么程序计数器记录的应该是正在执行的虚拟机字节码指令的地址;

    • 如果线程正在执行的是一个本地(Native)方法,程序计数器的值应该为空(Undefined);

    • 是唯一一个在《java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域

    虚拟机栈

    • 线程私有

    • 描述了java方法执行的线程内存模型:

      • 每个方法被执行时,java虚拟机栈会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息

      • 每一个方法被调用直至执行完毕的过程,都对应着一个栈帧从入栈到出栈的过程

    • 两种异常情况:

      • StackOverflowError:线程请求的栈深度大于虚拟机允许的深度时,会抛出StackOverflowError

      • OutOfMemoryError:如果虚拟机栈可动态拓展,当栈拓展时无法申请到足够的内存时会抛出OutOfMemoryError

    局部变量表

    • 存放了编译期可知的各种java虚拟机基本数据类型、对象引用和returnAddress类型

      • 基本数据类型(boolean byte char short int float long double)

      • 对象引用(引用地址或句柄)

      • returnAddress类型(指向一条字节码指令的地址)

    • 这类数据类型在局部变量表中的存储空间以局部变量槽(slot)表示

      • 64位的long和double类型的数据占用两个slot,其余类型占用一个

    • 局部变量表所需的内存空间在编译期间完成分配

      • 当进入一个方法时,需要在栈帧中分配多大空间的局部变量表是完全确定的,在方法运行期间不会改变

    本地方法栈

    • 虚拟机栈是为虚拟机执行java方法(字节码)服务,而本地方法栈是为虚拟机执行本地(Native)方法服务

    • 具体的虚拟机可以根据需要自由实现它,例如HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一

    • 两种异常情况,同虚拟机栈:

      • StackOverflowError:线程请求的栈深度大于虚拟机允许的深度时,会抛出StackOverflowError

      • OutOfMemoryError:如果虚拟机栈可动态拓展,当栈拓展时无法申请到足够的内存时会抛出OutOfMemoryError

    java堆

    • 是虚拟机所管理的内存最大的区域,存放对象实例和数组

    • 被所有线程共享,在虚拟机启动时创建

    • java堆是垃圾收集器管理的内存区域(GC堆)

    • 从内存分配的角度,由所有线程共享的java堆,可以划分成多个 线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),提升对象的分配效率

    • java堆可以处于连续的物理上不连续的内存空间

    • java堆可以被实现成固定大小,也可以被实现成可扩展大小,主流的虚拟机都是可扩展,通过参数:-Xmx、-Xms调节

    • OutOfMemoryError:如果堆中没有内存完成实例分配,且堆无法再扩展时,会抛出OutOfMemoryError异常

    方法区

    • 所有线程共享,用于存储已被虚拟机加载类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

    • JDK8之前 用“永久代”来实现方法区

      • 这样使得HopSpot的垃圾收集器能够向管理堆一样管理这部分内存,省去了专门为方法区编写内存管理代码的工作。

      • BEA 的 JRockit、IBM的 J9都不存在永久代的概念。

      • 使用“永久代”并不是一个好主意,这导致了:

        • java应用更容易遇到内存溢出的问题,因为永久代有 -XX:MaxPermSize 的上限,即使不设置也有默认大小,而 J9和 JRockit只要没有触碰到进程可用内存的上限,例如32位系统中的4GB就不会出现问题

        • 有极少数的方法,例如String::intern() 会因永久代的原因,在不同虚拟机下有不同的表现

    • 永久代与方法区在HotSpot的变化:

      • JDK6开始打算放弃永久代,逐步采用本地内存实现方法区;

      • JDK7把原本放在永久代的 字符串常量池、静态变量等移出(移到堆中);

      • JDK8完全废弃了永久代的概念,改用与 JReckit、J9一样在本地内存中实现的“元空间”

    • 这个区域的内存回收:目标主要针对常量池的回收和类型的卸载

    • OutOfMemoryError:如果方法区无法满足新的内存分配需要时,会抛出OutOfMemoryError异常

    运行时常量池

    • 是方法区的一部分

    • 用于存放编译器生成的各种字面量符号引用,部分内容将在类加载后存放在方法区的运行时常量池中。除了Class文件中描述的符号引用,还会把由符号引用翻译过来的直接引用放在运行时常量池中

    • 动态性,运行期间也会有新的常量放入池中 (如intern()方法)

    • OutOfMemoryError:如果常量池 无法满足新的内存分配需要时,会抛出OutOfMemoryError异常

    直接内存

    • 直接内存并不是虚拟机运行时数据区的一部分;

    • 也可能导致OutOfMemoryError异常。

    本机直接内存的分配不受java堆大小的限制

    NIO

    https://www.jianshu.com/p/5bb812ca5f8e

    NIO(JDK1.4)模型是一种同步非阻塞IO,主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(多路复用器)。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(多路复用器)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

    基于通道(Channel)与缓冲区(Buffer)的I/O方式。

    它可以使用Native本地库直接分配堆外内存,然后通过存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。避免了在java堆和Native堆之间来回复制数据,提高了性能。

  • 相关阅读:
    《架构整洁之道》阅读笔记
    设计模式——单例模式
    设计模式——工厂模式使用
    记一次同事安装我自定义一Python3 SDK在window10安装失败情况
    CentOS7常用的一些命令
    Java
    Java
    Java
    Java
    python
  • 原文地址:https://www.cnblogs.com/learnjavajava/p/14505314.html
Copyright © 2011-2022 走看看