一。内存区域与内存溢出异常
1. Java虚拟机的运行时数据区:
-----------------------------------
运行时区域
方法区 虚拟机栈 本地方法栈
堆 程序计数器
————————————————
执行引擎 --> 本地库接口 --> 本地方法库 (线程共享)
2. 程序计数器PC
控制字节码解释器,执行字节码;由于Java的多线程是通过线程轮流分配处理器执行时间实现的,因此每个线程有一个程序计数器;
如果是Java方法,则记录的是字节码的地址;如果是Native方法,则为空;
3. Java虚拟机栈
内存分为堆和栈,栈就是指的虚拟机栈;
每个方法被调用时创建一个栈帧,存放局部变量、操作栈、方法出口;
局部变量表里存放了基本数据类型、对象引用和返回地址;
如果线程申请的栈深度超出了允许的深度,抛出StackOverflowError,如果虚拟机栈因冬天扩展无法申请到足够的内存,抛出outofmemoryerror.
4. 本地方法栈
和Java虚拟机栈类似,为调用Native方法服务;
5. 堆
Java的堆是被所有线程共享的一块内存区域,虚拟机启动时创建;其目的是存放对象实例;
堆是垃圾收集器的主要管理区域;现在的算法是分代回收算法,分为:新生代和老生代;再细分:Eden, From survivor, to survivor空间;
从内存分配来看,Java堆中还可分成多个线程私有的分配缓冲区;
物理不连续,逻辑连续;
6. 方法区
永久区,存放常量池和类型的卸载;线程共享;
7. 运行时常量池:在方法区;动态性;
8. 直接内存:采用Native库分配堆外内存,存储在Java堆里的DirectByteBuffer对象作为引用;避免在Native堆和Java堆中交换数据
二。对象访问及溢出
object obj = new object()
obj 本地变量区;new object 在Java堆,Java堆中还有类型数据的地址信息(方法、父类、接口等);
有两种实现方式,采用句柄或者直接指针的方式进行访问;
句柄:引用 (本地变量表) -> Java堆的句柄池 ->Java堆的实例池 和 方法区的数据
指针:引用->Java堆的对象实例数据 (含有方法的指针) ->方法区的类型数据
-Xmx -Xms 堆的最大和最小 -Xoss 栈的大小
反复调用函数是栈溢出;反复申请空间是堆溢出;不断在常量池添加常量,导致常量池溢出;不断注册新的方法,导致方法区溢出;使用Unsafe分配本地内存,导致溢出;
三。垃圾回收及内存分配
1. 判断对象死亡的方法:
引用计数方法:无法解决互相引用的情况
根搜索算法(Java C# Lisp中都用的这个):作为跟的对象:虚拟机栈里的对象,方法去中类静态属性对象,常量引用对象,本地方法JNI的引用的对象;
如果一个对象已被执行finalize 或者 没有重载,则第一次标记;如果一个对象重写了必须执行的finalize()函数,则执行,如果执行过程中重新建立联系,则移除Fqueue,否则进行第二次标记,执行释放空间;但finalize方法只被执行一次!
可以调用两次System.gc;并在finalize中使某个引用指向自己,造成一次无法释放,一次释放的假象;
2. 方法区
废弃常量和死亡对象差不多;
废弃的类需要判断:类的所有实例都被回收,java堆中不存在实例;c该类的classloader已被回收;class没有被引用,也不能通过反射得到该类;
使用反射、动态代理等场景,需要永生代回收;
标记-清除算法:最基础的,标记需要释放的,逐个释放
复制算法:内存分成两块,将需要释放的一块的活的对象复制到另一块,直接释放一整块内存
标记整理算法:先标记,再移动内存
分代收集算法:将内存分为新生代和老生代,新生代,存活较少,采用复制算法;老生代采用标记-清除或标记-整理算法;
三。垃圾收集器
Serial 收集器:单线程工作,收集工作进行时,停止其他所有的工作线程(Client模式下新生代);(复制算法)
ParNew 收集器:多线程工作(server模式下新生代,多核)
Parallel Scavenge 收集器:新生代,多线程收集器(目标在与达到最大吞吐量,吞吐量=(用户代码时间)/总时间),复制算法
Serial Old 收集器:老年代收集器,标记整理算法;
Paralle Old: 老年代,多线程,标记整理
CMS (concurrent mark sweep): 最短时间为目标;标记清除算法;
特点:1.与用户线程并行;2.对CPU资源敏感;3.无法处理浮动垃圾(标记之后用户线程产生的垃圾);4. 基于标记-清除,产生内存碎片,不利于大内存的分配;
G1 (Garbage First): 标记-整理算法,无碎片;非常精确的控制停顿;
四。内存分配策略
1. 对象优先存在新生代;2.大对象直接进入老生代;3.长期存活按年限进入老生代(躲过一次minorGC长一岁);
minorGC是将新生代的对象分配到老生代;
五。工具
1. 常用的数据:运行日志,异常堆栈,GC日志,线程快照,堆转存快照;
2. sun JDK 1.6 监控和故障处理的工具
jps JVM process status tool, 查询虚拟机进程,显示主类;
jstat Jvm statistics monitoring tool, 显示虚拟机的类装在,内存,垃圾收集,JIT编译等数据;
jinfo 实时配置和查看虚拟机的参数
jmap java内存映像工具,用于生产堆转储快照;还可查询finalize执行队列;Java堆、和永久代的详细信息;
jhat java虚拟机转储快照分析工具
jstack 堆栈分析工具
3. 可视化
JConsole : Java监视与管理控制台: Eden 和Survivor默认比8:1
VisualVM + BTrace分析日志VisualVM插件