JVM内存管理

来源:互联网 发布:协同过滤算法python包 编辑:程序博客网 时间:2024/06/07 17:16

JVM内存管理

内存结构

  • JVM是按照运行时数据的存储结构来划分内存结构的,JVM在运行java程序时,将它们划分成几种不同格式的数据,分别存储在不同的区域,这些数据统一称为运行时数据。运行时数据包括Java程序本身的数据信息和JVM运行Java需要的额外数据信息。

  • 在java虚拟机规范中,将java运行时数据划分为五种,分别为

    • 程序计数器
    • java虚拟机栈
    • 本地方法栈
    • 方法区
  • 程序计数器
    • 程序计数器是一块线程私有的内存区域,它是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变程序计数器的值来选取下一条要执行的字节码指令,分支、循环、跳转、异常跳转、线程恢复等基础功能都需要依赖这个计数器来完成。由于Java虚拟机的多线程时通过线程切换并分配处理器执行时间来实现的,对于单核处理器在某一个时间都只会有一个线程在运行,为了线程切换后能恢复到正确的执行位置,每个线程都需要维护一个独立的程序计数器,各个线程之间的计数器互不影响,独立存储。如果当前线程正在执行一个Java方法,程序计数器记录的是正在执行的虚拟机字节码指令的指令。如果当前线程正在执行一个Native方法,程序计数器记录值则为空。
      • java 虚拟机栈
    • Java虚拟机栈是一块线程私有的内存区域,它总是和某个线程关联在一起,每当创建一个线程时,JVM就会为其创建一个对应的Java虚拟机栈,用于存储Java方法执行时用到的局部变量表、操作数栈、动态链接、方法出口等信息。Java虚拟机栈中会包含多个栈帧,每个栈帧又会于某个方法关联起来,每运行一个方法就会创建一个栈帧(用于存储局部变量表、操作数栈、方法返回值等信息,局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间时完全确定的,在方法运行期间不会改变局部变量表的大小)。每当一个方法执行时,创建一个栈帧置于栈顶。每当一个方法执行完成时,这个栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧。Java虚拟机栈栈顶的栈帧就是当前正在执行的活动栈帧,也就是当前Java虚拟机正在执行的方法,程序计数器也会指向这个地址。只有活动的栈帧的局部变量可以被操作栈使用,当这个栈帧调用另一个方法时,与新方法对应的新栈帧也随之被创建,并放到Java虚拟栈的顶部,变为新的活动栈帧,当这个栈帧执行完成时这个栈帧移出Java栈,刚才的那个栈帧又变为活动栈帧,前面栈帧的返回值又变为这个栈帧的操作栈中的一个操作数。如果前面的栈帧没有返回值,那么当前的栈帧的操作栈的操作数就不会有任何变化。
      • 本地方法区
    • 本地方法栈是为JVM运行Native方法使用的空间,它也是线程私有的内存区域,它的作用与上一小节的Java虚拟机栈的作用是类似的。除了代码中包含的常规的Native方法会使用这个存储空间,在JVM利用JIT技术时会将一些Java方法重新编译为NativeCode代码,这些编译后的本地方法代码也是利用这个栈来跟踪方法的执行状态。
      • Java堆JVM所管理的内存中最大的一块,是JVM管理Java对象的核心存储区域,它是被所有线程共享的一块内存区域,在JVM启动时创建。Java堆是存储Java对象实例的地方,是Java应用程序与内存关系最密切的存储区域。每一个存储在堆中的Java对象多时这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性。根据JVM规范,JVM堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,可通过-Xmx设置最大Java堆的大小,-Xms设置初始化时Java堆大小
      • 方法区
      • 方法区是线程共享的内存区域,它用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区也就是我们通常所说的永久区,它的大小可通过参数—XX:PermSize、—XX:MaxPermSize进行设置,方法区存储区域的大小一般在程序启动后一段时间内就固定了,JVM运行一段时间后,需要加载的类通常都已经加载到JVM中了,但项目中如果存在类的动态编译,就需要观察方法区的大小是否能够满足类存储。垃圾回收较少发生在该内存区域,它存储的信息相对比较稳定,回收的主要目标是常量池和对类型的卸载。
      • 对类型的卸载相当苛刻,要求满足以下三个条件才能算是“无用”的类
        • 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
        • 加载该类的ClassLoader已经被回收
        • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

JVM栈和堆内存的分配

   在操作系统中内存分配策略为三种:1. 静态内存分配;2. 栈内存分配;3. 堆内存分配。   JVM内存分配主要基于两种,分别是**栈内存分配****堆内存分配**
  • 栈内存分配

    • Java栈空间的分配是和线程绑定在一起的,当一个线程创建时,JVM就会就会为这个线程创建一个新的Java栈,一个线程的方法的调用和返回对应于这个Java栈的压栈和出栈。当线程激活一个Java方法时,JVM就会在线程的Java栈里新压入一个栈帧,用来保存参数、局部变量、中间计算过程和其他数据。退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁。

    • 栈的优点:存取速度比堆快,仅次于寄存器,栈数据可以共享。

    • 栈的缺点:存在栈中的数据大小、生存期是在编译时就确定的,导致其缺乏灵活性。
  • 堆内存分配

    • 每一个Java应用都唯一对应一个JVM实例,每一个实例又对应一个堆。Java应用程序在运行中所创建的几乎所有的类实例或数组都会放在这个堆中(随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换让一些对象直接在栈上进行分配,从而提升程序性能),并由该应用程序的所有线程所共享。Java中分配堆空间时在启动时自动初始化的,对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码显示地释放,由垃圾回收机制负责回收这些内容区域。

    • 堆的优点:动态地分配内存大小,生存期不必事先告诉编译器,它是在运行期动态分配的,垃圾回收器会自动收走不再使用的空间区域。

    • 堆的缺点:运行时动态分配内存,在分配和销毁时都要占用时间,因此堆的效率较低。

内存分配与回收

 就Java语言而言,内存分配主要包括**静态内存分配****动态内存分配**
  • 静态内存分配是指在Java被编译时就已经确定了所需的内存空间,当程序被加载时系统把内存一次性分配给它,在程序执行时不发生变化,程序执行结束时内存被回首。例如:程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出不停的进栈和出栈。这几个区域的分配分配和回收都具有确定性,无需过多考虑。

  • 动态内存分配是指Java中的对象内存空间是在运行期间动态分配的,只有程序处于执行期间才会知道哪些对象被创建。这部分内存空间什么时候不被使用,如果回收它们,正是JVM垃圾回收需要解决的问题。

下章详细介绍内存分配与回收

1 0
原创粉丝点击