Java的内存管理采用[自动内存管理]机制,因为这个自动管理机制,Java程序员就不需要去写释放内存的
代码,而且不容易出现内存泄漏问题(比C/C++程序员少一些烦恼)。但是由于内存的申请和释放都交给了
Java虚拟机,一旦出现内存泄漏和溢出问题时,在不了解Java虚拟机内存结构和自动管理机制的情况下,就
很难排查问题的所在。所以如果想要成为一个优秀的程序员或者进阶为一个牛逼的架构师,掌握Java虚拟机
的自动内存管理机制那是必须的。
摘自:https://www.cnblogs.com/aflyun/p/10575740.html
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
栈是由一个个的栈帧所组成的,每个栈帧中存储了一下信息:
局部变量表、操作数栈、动态链接、方法出口、其他附加信息
局部变量表
定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量.局部变量表中的变量只在当前方法调用中有效。在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后,随着方法栈帧的销毁,局部变量表也会随之销毁。局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收
操作数栈
操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译器就定义好了,保存在方法的code属性中,为max_stack的值。
动态链接
在Java源文件被编译成字节码文件中时,所有的变量和方法引用都作为符号引用(symbolic Refenrence)保存在class字节码文件(javap反编译查看)的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用(#)最终转换为调用方法的直接引用。
参考文章
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
3.运行时常量池位于方法去中,但是 JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。而且从jdk1.8开始,永久代被元空间所取代,元空间不在占用堆内存,而是直接使用本地内存。
有关一些概念的讲解阐述参考:https://www.cnblogs.com/aflyun/p/10575740.html
使用idea调整堆内存大小
说一下自己测试的时候出现的情况,
-Xms1024m -Xmx1024m -XX:+PrintGCDetails//使用这个命令修改虚拟机参数并打印GC信息。
第一个就是最大内存的大小一定要大于或等于最小内存的大小。
内存很小的时候会发生内存溢出
内存较小即使不发生内存溢出也会导致频繁的进行GC,导致程序执行的效率低下。
内存大的时候GC的次数少,所以执行很快。
这个时候我们打印出来了GC的情况,但是还是不能够知道怎么进行调优,
安装一个插件帮助我们进行查找代码中哪些地方会导致内存占用,使用这个插件还要去官方获取客户端进行安装。这里不过多赘述。
-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
使用上面的命令进行dump文件
发生这个错误后就会dump文件
双击之后就会用我们刚才安装的客户端打开了
-Xms1m :设置初始化内存大小
-Xmx8m :设置最大内存的大小
-XX:+HeapDumpOnOutOfMemoryError:发=发生错误时候dump文件
-XX:+PrintGCDetails:打印GC的信息。
GC垃圾回收算法
- 标记-清除:
这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被
回收的对象,然后统一回收。这种方法徆简单,但是会有两个主要问题:1.效率丌
高,标记和清除的效率都徆低;2.会产生大量丌连续的内存碎片,导致以后程序在
分配较大的对象时,由亍没有充足的连续内存而提前触发一次 GC 劢作。 - 复制算法:
为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只
使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然
后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,
内存的代价太高,每次基本上都要浪费一般的内存。
亍是将该算法迚行了改迚,内存区域丌再是按照 1:1 去划分,而是将内存划分为
8:1:1 三部分,较大那仹内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。
每次都会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然
后清除 Eden 区,如果此时存活的对象太多,以至亍 Survivor 丌够时,会将这些对
象通过分配担保机制复制到老年代中。(java 堆又分为新生代和老年代) - 标记-整理
该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高
时,也解决了复制算法的效率问题。它的丌同乊处就是在清除对象的时候现将可回
收对象移劢到一端,然后清除掉端边界以外的对象,这样就丌会产生内存碎片了。 - 分代收集
现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生
代和老年代。在新生代中,由亍对象生存期短,每次回收都会有大量对象死去,那
么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间迚行分配担
保,所以可以使用标记-整理 或者 标记-清除。