一、JVM的简单架构
JVM包括4个部分,类加载器(classloader),运行数据区(runtime data area),执行引擎(execution engine)和本地接口(native interface)组成
其中重要部分为运行时区,只要包括五大组件:
堆:heap
栈:Java stack
方法区:method area
本地方法栈:native method stack
程序计数器:program counter register
其中堆和方法区为线程共享,其余为线程私有
程序方法执行流程:
类加载器进行加载(class文件)->在方法区记录元数据,包括静态常量->对象、数组存入堆中->方法执行,形成栈帧进行压栈,方法执行完毕,出栈,资源释放
二、垃圾回收过程
由于方法区和堆是线程共享,需要进行垃圾回收,及时释放资源。其中方法区位于永久带,不需要考虑垃圾回收,GC主要发生在堆中。
堆得构成:年轻带、老年带和永久带
在JDK8后,没有永久带了,class文件和方法元数据存放在元空间(matespace),元空间直接使用本地内存
年轻带包含Eden和2个survivor区,老年带就一个区域
垃圾回收过程为:
<1>对象和数组创建后进入Eden区和s1(from区域),在空间存满后,新的对象的创建,会引发小GC(minorGC),此时标记的存活对象(包括Eden和s1)会被复制到s2(to区域),然后清空Eden和s1,minorGC不会引发stop-the-world
<2>如果多次(默认15)回收后任然不能回收的对象会进入老年带(old generation),或者minorGC时无法在s2中存储的对象、数组也会进入老年带。
<3>然后重复过程<1><2>,知道老年带空间存满,会引发FullGC,产生STW现象,如果fullGC后,空间仍然不够,会引发oom异常
垃圾标记算法:
引用计数法(缺点:不能标记循环调用的对象)
根搜索方法(可达性分析法)
垃圾回收算法:
<1>标记-清除法
<2>复制算法
<3>标记整理算法
垃圾回收器:
主要包括:串行、并行、CMS、G1等
<1>串行垃圾回收器
<2>并行垃圾回收器
<3>CMS收集器
对于分布式计算来说,垃圾回收器需要采用CMS,对于内存依赖较大的Hbase、spark等,需要增加heap内存,以保证性能
调节参数看这里
两个问题:
<1>写一段代码,让他报堆内存溢出: 不断的创建对象,你要去用这些对象
package threadTest;
import java.util.ArrayList;
public class HeapMemoryOut {
public static void main(String[] args) {
ArrayList<String> a = new ArrayList<>();
int count = 0;
while (true){
a.add(count+"");
count++;
System.out.println(count);
}
}
}
调节堆内存大小:
-Xms10m -Xmx10m
运行后:Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
出现OOM
<2>写一段代码,让他报栈内存溢出 递归一直调用即可
package threadTest;
public class StackMemoryOut {
public static void f(){
int count = 0;
f();
}
public static void main(String[] args) {
f();
}
}
Exception in thread "main" java.lang.StackOverflowError