JVM内存管理基础
来源:互联网 发布:手机恢复数据免费 编辑:程序博客网 时间:2024/06/08 06:18
内存管理基础
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内存管理基础
- 【JVM基础】内存管理
- jvm基础-内存管理
- JVM基础(三) JVM内存管理
- JVM自动内存管理--内存区域基础概念
- Java基础---JVM内存管理以及垃圾回收机制
- JVM基础 之Java HotSpot虚拟机中的内存管理
- JVM基础 之java内存管理以及GC
- JVM基础 之Java HotSpot虚拟机中的内存管理
- JVM内存管理总结
- JVM内存管理
- JVM内存管理总结
- JVM内存管理
- JVM内存管理
- JVM内存管理
- JVM&内存管理
- jvm内存管理
- JVM内存管理
- 软件工程笔记整理
- spring中配置DBCP BasicDataSource的方式
- Spring Security java配置与XML配置的对应和转换
- 【常用模板】 混合背包
- codesmart在vs6中似乎只有vb6的版本,那就用Visual Assist做补充吧。其实mztools
- JVM内存管理基础
- 数据库连接奇葩错误
- MySql四舍五入
- 栅格服务封装
- 多重背包
- Faster RCNN详解:从region proposal到bounding box回归
- php smarty模板引擎的使用
- Charles 从入门到精通
- 【linux】比较不错的常用命令(长期维护)