JVM内存管理基础
2016-12-14 16:05
211 查看
内存管理基础
From 深入分析JavaWeb一个Java进程的哪些部分需要分配内存
Java堆
用于存储Java对象的区域,需要分配内存,堆大小在JVM启动时就向操作系统申请了。通过-Xmx和-Xms来控制大小,前者表示最大的堆的大小,后者表示初始化的堆的大小。JVM管理着堆内存,对象创建由应用程序控制,而对空间的释放由GC垃圾收集器来完成。根据GC算法的不同,回收的时机也不尽相同。
线程
JVM运行程序的实体就是线程。所以需要一些内存来存储线程运行所需要的数据。JVM会为每一个线程构建一个线程栈,通常在256K~756KB之间。类和类加载器
Class对象和加载Class的ClassLoader对象需要空间存储,他们被存储在称为PermGen区,永生代。PermGen永生代的内存回收需要满足3个条件:
1,在Java堆中没有对表示该类加载器的Java.lang.ClassLoader对象的引用。 2,Java堆没有对表示类加载器的类的任何java.lang.Class对象的引用。 3,在Java堆上该类加载器加载的任何类的所有对象都不在存活。
所以Bootstrap ClassLoader、APPClassLoader和ExtClassLoader都不满足这些条件,在程序运行时无论是系统类还是程序加载的类都不会在运行时释放。
NIO
在java1.4以后加入了NIO(new io),引入了一种基于通道和缓冲区来执行IO的方式,NIO使用java.nio.ByteBuffer,allocateDirect()方法分配内存,这种方法分配的内存是本机的内存,而不是JVM最开始的-Xmx内存。这类方法直接在底层调用os:malloc()方法。本地调用JNI
Java Native Interface,Java本地调用c/c++方法,如一些文件操作,网络IO和其他一些系统调用会占用一定的内存。JVM内存体系结构
PC寄存器
PC寄存器应该说是一种数据结构更合适,用于保存当前正常执行的程序的内存地址。java是多线程的,所以线程可能不会一直线性执行下去,多个线程交叉执行的时候,被中断的线程执行到哪里的内存地址需要保存起来,以便中断结束后继续执行。Java栈-栈帧
JVM为每一个线程分配一个栈,而在这个栈中又会形成多个栈帧,一个栈帧对应与一个方法调用,正在执行的方法放在栈帧的顶部,这个地址也是PC寄存器指向的地址,在方法内部调用方法就会形成新的栈帧继续放在顶部。而在栈帧返回后,将数据返回给前一个栈帧,这就是退栈的过程。由于java栈与java线程对应起来的,所以数据不是共享的,故不用关心数据一致性过程,也不会有同步锁的问题。堆-Java对象存储的地方
堆是存储Java对象的地方,是核心存储区域,每一个存储在堆中的对象,都会是这个对象的类的一个副本,继承了从他父类开始的所有非静态属性。堆是线程共享的,所有可能需要通过同步加锁来保持一致性问题。方法区
常量池,域,方法数据,方法体,构造函数体,类的专用方法,实例初始化,接口初始化。这个方法区实际上也是java堆的一部分,这个区叫做“PermGen”。方法区是逻辑上的独立而已!运行时常量池,Runtime Constant Pool代表运行每个Class文件的常量表。编译器的数字常量、方法等。
本地方法区
JVM运行Native方法时准备的空间,因为通常Native方法都是用C实现的,所以本称为C栈。内存回收检测
Garbage Collection:垃圾收集器,主要完成2部分功能,一是检测出垃圾对象,二是释放垃圾对象占用的空间。当一个对象不再被其他活动对象所引用的时候,那么这个对象就可以被回收了,活动对象值得是能有一条到根对象集合的对象。如下图所示:例如下图的g和h对象,虽然h对象被g对象引用,但是g对象不能达到根对象节点,所以这2个对象是非活动对象,可以被垃圾收集器回收。
根对象集合的内容
在方法中局部变量区的对象的引用:比如定义在play()方法的局部Date对象,这个对象直接存储栈帧的局部变量区中。在java操作栈中的对象引用:这些对象在java操作栈中,肯定是包含在根对象中的。
在常量池中的对象引用:每一个类都包含一个常量池,通常会有很多对象引用,如表示类名字的字符串就在此。
本地方法持有的对象引用:在调用native方法,但这些对象还没有被释放时。
类的Class对象:每当JVM加载一个类的时候,都会创建代表这个类的Class对象,同样是放在堆中。而这个类当不再被使用时,也同样可以被回收。
Hotspot分代垃圾收集算法
设计思路
将对象根据寿命的长短来分类,分成Young和Old组,最开始新建的对象都放在Young区,经过数次垃圾收集后依然存在则应该将其放入Old区,Old区由于是寿命较长的对象,垃圾收集并不是那么频繁,这样就提高了垃圾回收的效率。Java将堆分成的3个部分,Young区,Old区和Perm区。
Young区分为Eden和两个Survivor区,Survivor分为From区和To区。最开始所创建的对象都在Eden区,当Eden区满以后触发minor GC将Eden区仍然存活的对象复制到To区,然后将Survivor的From区对象也都复制存放到To区,这样From区为空,此时将From区重命名为To区,将To区重命名为From区。始终保证有一个Survivor区为空。(书上说是复制到其中一个Survivor区,我结合Node.js的知识和FromTo区,自己猜测阐述了上述说法,不一定正确,但是这样理解比较容易些吧,不然复制的时候怎么知道要复制到哪里呢?)
Old区:存放的是当Survivor区满的时候触发minor GC操作后仍然存活的对象;当Eden区满且Survivor区容不下这些对象的时候也会放到Old区。如果Old区满了,那就触发Full GC大清理对象。
PermGen区:主要是用来存放类的Class对象和ClassLoader对象,如果一个类被频繁加载,也可能导致PermGen满,此时会触发Full GC。
如果使用不同的ClassLoader实例加载同一个类会怎么样,会不会导致JVM的PermGen(永久代)n区无限增大??答案是否定的,why,因为ClassLoader对象也是对象,在没有被持有引用的时候,也会被JVM回收。值得注意的是,被ClassLoader加载的类的字节码会一直保存在JVM的PermGen中,这个数据一般是在Full GC的时候才会被回收,所以,如果应用大量使用动态类加载。Full GC又不是太频繁,也要注意PermGen的大小,防止内存溢出。
堆建议
Sun公司给的堆大小建议是Young区占总堆的1/4,而Survivor区占整个Young区的1/8。存在三类垃圾收集算法:
Serial Collector
Parallel Collector
CMS Collector
不会讲。讲不好。不讲了。
相关文章推荐
- JVM基础 之java内存管理以及GC
- JVM基础 之Java HotSpot虚拟机中的内存管理
- JVM基础之java内存管理以及GC
- JVM基础(2)——内存管理
- JVM基础 之Java HotSpot虚拟机中的内存管理
- C++内存管理基础之new & delete
- WINCE内存管理基础
- 从JVM内存管理的角度谈谈静态方法和静态属性
- JVM基础概念总结:数据类型、堆与栈
- JVM基础概念总结:数据类型、堆与栈
- Java EE应用中的性能问题解决方案 — 第一部分 内存溢出和JVM内存管理内幕(B)
- linux 内存管理之基础篇
- JVM结构基础(一)
- Java基础:JVM(Java 虚拟机)的详细讲解
- C++内存管理基础之new & delete
- CORBA对象生命周期之实现和内存管理-Java基础-Java-编程开发
- 从JVM内存管理的角度谈谈静态方法和静态属性
- Java(JVM)虚拟机结构基础,和JAR文件包及jar命令详解
- JVM结构基础(一)
- C++内存管理基础之new & delete