Java内存管理机制深入浅出

162 阅读6分钟

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战 !

一,程序内存管理: 在这里,我们要引出三个组件:Java 程序,Java 虚拟机与操作系统。Java 程序运行需要 JVM,JVM 又与操作系统交互获取内存或者释放内存。这里我们重点掌握三者之间使用分配内存的相互关系。我们从程序运行前,程序运行开始,与程序运行内存溢出三个阶段讲解。 程序运行前:有些编程语言编写的程序会直接向操作系统请求内存,但 Java并不支持那么做。当程序准备执行时,由 JVM 向操作系统请求一定的内存空间,称为初始内存空间。程序执行过程中所需的内存都由 Java 虚拟机从这片内存空间中划分。这样做的一个重要优点是保证了程序的平台无关性。 如下图: 在这里插入图片描述 程序运行中:Java 程序一直向 Java 虚拟机申请内存,当程序所需内存空间超出初始内存空间时,Java 虚拟机会再次向操作系统申请更多的内存供程序使用。 在这里插入图片描述 内存溢出:程序接着运行,当 Java 虚拟机已申请的内存达到了规定的最大 内存空间,但程序还需要更多的内存,这时会出现内存溢出的错误。 在这里插入图片描述 至此可以看出,Java 程序所使用的内存是由 Java 虚拟机进行管理、分配的。 Java 虚拟机规定了 Java 程序的初始内存空间和最大内存空间,开发者只需要关心 Java 虚拟机是如何管理内存空间的,而不用关心某一种操作系统是如何管理内存的。 二:内存空间逻辑划分:方法区,堆与栈 对于大多数人来说,我们经常喜欢把Java内存分为堆,栈和方法区 1.方法区: 方法区的内存我们在程序开发中关注较少。java 中需要占用内存的,除去 变量外,其实还有一个重要的东西就是类结构,比如我们开发了一个 Student类,那么在创建 Student 对象之前,JVM 类加载器会把 Student 类加载到内存,称为类结构(或者叫模板),当我们创建对象时,使用这个类结构类模板。方法区默认最大容量为 64M, 方法区主要存储:类的结构,静态属性与静态方法。编写中小型程序时,一般不会造成方法区的内存溢出。 2.堆: 堆默认最大容量为 64M,堆存放对象持有的数据,同时保持对原类的引用。可以简单的理解为对象属性的值保存在堆中,对象调用的方法保存在方法区。 3.栈 栈默认最大容量为 1M,在程序运行时,每当遇到方法调用时,Java 虚拟机 就会在栈中划分一块内存称为栈帧(Stack frame),栈帧中的内存供局部变量(包括基本类型与引用类型)使用,当方法调用结束后,Java 虚拟机会收回此栈帧占用的内存。也就是说:栈中主要存储8种基本数据类型以及对象的引用(即指向堆内存的地址)如图: 在这里插入图片描述 三:但实际上,这种分发很粗糙,Java内存的划分远比这复杂,如图. 在这里插入图片描述 1.程序计数器: 程序计数器是一块较小的内存空间,字节码解释器就是通过它来选取需要执行的字节码指令,内存私有(即每个线程之间的计数器互不影响,独立存储),这样设计师为了解决多线程在切换后能恢复的确认位置而设立的,因为Java虚拟机的多线程是通过轮流切换并分配处理器实现的 2.Java虚拟机栈 此块区域也是线程私有的它的生命周期与线程相同,每个java方法在执行时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息.而上面所提到的栈即是此java虚拟机栈,更确切的说是虚拟机栈中的局部变量表部分, 主要存储8种基本数据类型以及对象引用 3.本地方法栈: 与虚拟机栈发挥的作用很相似,但也是有区别的,虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机使用native方法服务.因为其余java虚拟机很相似,故以有的虚拟机直接将本地方法栈和Java虚拟机栈合二为一(l例如:Sun HotSpot虚拟机) 4.java堆 java堆是被所有线程共享的一块区域,在虚拟机启动是创建,它存在的唯一目的就是存放对象几乎所有对象实例都在此分配内存,(在此可了解一下JIT)另外,java堆是垃圾回收车管理的主要区域,(其实由于收集器基本都采用分代收集算法,致使java堆还可继续细分:新生代和老生代), 5方法区: 线程共享区域,它用于,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,垃圾回收区域在这块较少出现,,此区域内存回收的主要目标是针对常量池的回收和对类型的卸载 6.运行时常量池 它是方法区的一部分,此区域主要用来存放编译器生成的各种字面量和符号引用,一般来说,Class文件中翻译出来的直接引用也会存储在此,字面量和符号引用是什么呢?

  • 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等; 字面量也可以理解为实际值,int a = 8中的8和String a = "hello"中的hello都是字面量
  • 符号引用包括:1.类和方法的全限定名2.字段的名称和描述符 3.方法的名称和描述符。 符号引用就是一个字符串,这个字符串包含足够的信息,以供实际使用时可以找到相应的位置。你比如说某个方法的符号引用,如:“java/io/PrintStream.println:(Ljava/lang/String;)”。里面有类的信息,方法名,方法参数等信息。 7.直接内存 并不是虚拟机运行时数据区的一部分,但它却被频繁的使用,且很可能导致OutOfMemory异常!

欢迎关注笔者公众号互相交流!

image.png

\