Java虚拟机
Java内存区域与内存溢出异常
-
程序计数器:可以看作是当前线程所执行的字节码的行号指示器。线程私有。
-
Java虚拟机栈:线程私有。用于存储局部变量,操作数栈,动态链接,方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。局部变量中存储了Java的基本数据类型和对象引用。
-
本地方法栈:与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为了虚拟机使用到本地方法。
-
Java堆:线程共享。被所有线程共享的一块内存区域。是垃圾收集器管理的内存区域。对象实例及数组都在堆上分配。
-
方法区:线程共享。用于存储被虚拟机加载的类型信息,常量。静态变量,即时编译的代码缓存。
-
运行时常量池:用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
-
直接内存:并不是虚拟机运行时数据区的一部分,但是这部分内存也被频繁使用。
对象访问机制
-
句柄访问:句柄访问的好处是reference中存储的是稳定的句柄地址,在对象被移动时只会改变实例数据地址,而reference本身不需要改变。
-
直接访问:访问速度快,节省了一次指针定位的时间开销。
对象存活算法
-
引用计数法
-
可达性分析:从GC Roots开始,如果对象与GC Roots间没有引用链,则对象不可达,将不可能再被使用
对象引用
-
强引用:只要引用关系在,就不会被垃圾收集器回收
-
软引用:还有一些用,但是非必须的对象,在系统发生内存溢出前才会把这些对象进行回收。
-
弱引用:生存到下一次垃圾收集为止。
-
虚引用:为了能在对象被回收时收到一个系统通知。
垃圾收集器
垃圾收集器
-
分代收集:新生代标记清除,老年代标记整理
-
标记-清除:标记在用的,清除未标记的
-
标记-复制:将存活对象复制到另外一块内存上
-
标记-整理:先标记,将标记的对象整理到一端,对另一端进行清除
经典的垃圾回收器
串行新生代收集器、串行老年代收集器、并行新生代收集器、并行老年代收集器、CMS
-
Serial:新生代采用复制算法,老年代采用标记-整理
-
ParNew:新生代采用复制算法,老年代采用标记-整理
-
Parallel Scavenge:基于标记-复制算法实现
-
Serial Old:新生代采用复制算法,老年代采用标记-整理
-
Parallel Old:基于标记-整理算法实现
-
CMS:基于标记-清除算法实现
对象内存分配与回收策略
-
对象优先在Eden分配
-
大对象直接进入老年代
-
长期存活的对象将进入老年代:-XX:MaxTenuringThreadhold 对象晋升老年代阈值
-
动态对象年龄判断:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象可以直接进入老年代。
Java堆内存划分
Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。
新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
新生代 1/3 老年代 2/3
默认的,Edem : from : to = 8 :1 : 1 ( 可以通过参数–XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的
新生代空间大小,from = to = 1/10 的新生代空间大小。
类加载机制
加载、验证、准备、解析、初始化、使用、卸载
验证:文件格式验证、元数据验证、字节码验证、符号引用验证
类加载器
类加载采用双亲委派模型:类加载器收到加载请求,自己不会加载,而是向上交给父类加载,如果父类加载不了,再由自己加载。
启动类加载器 Bootstrap Class Loader
扩展类加载器 Extension Class Loader
应用程序类加载器 Application Class Loader
自定义类加载器 User Class Loader
常用参数
|参数|描述|
| ------------------------------- | ------------------------------------------------------------ |
| -Xms | 初始堆大小。如:-Xms256m |
| -Xmx | 最大堆大小。如:-Xmx512m |
| -Xmn | 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90% |
| -Xss | JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。 |
| -XX:NewRatio | 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3 |
| -XX:SurvivorRatio | 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10 |
| -XX:PermSize | 永久代(方法区)的初始大小 |
| -XX:MaxPermSize | 永久代(方法区)的最大值 |
| -XX:+PrintGCDetails | 打印 GC 信息 |
| -XX:+HeapDumpOnOutOfMemoryError | 让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用 |
Java 锁
java lock 实现的原理:底层都是依托于AbstractQueuedSynchronizer,AbstractQueuedSynchronizer中抽象了绝大多数Lock的功能,而只把tryAcquire方法延迟到子类中实现。
AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态,经过调查线程的显式阻塞是通过调用LockSupport.park()完成,而LockSupport.park()则调用sun.misc.Unsafe.park()本地方法,再进一步,HotSpot在Linux中中通过调用pthread_mutex_lock函数把线程交给系统内核进行阻塞。
简单来说就是维护了一个线程获取锁状态和一个虚拟队列。
JVM调优
如何实现自定义加载器
实现自定义类加载有以下两步:
1、继承ClassLoader
2、重写findClass,在findClass里获取类的字节码,并调用ClassLoader中的defineClass方法来加载类,获取class对象。
注意:如果要打破双亲委派机制,需要重写loadClass方法。
如下:是一个自定义 的类加载器
public static class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data=null;
try {
data= loadByte(name);
} catch (IOException e) {
e.printStackTrace();
}
return this.defineClass(data,0,data.length);
}
private byte[] loadByte(String name) throws IOException {
File file = new File("/Users/admin/test/"+name);
FileInputStream fi = new FileInputStream(file);
int len = fi.available();
byte[] b = new byte[len];
fi.read(b);
return b;
}
}
下面是要加载的类:
public class Demo{
public void say(){
System.out.println("hello");
}
}
该类编译后的class 文件放置在/Users/admin/test/下,然后执行如下代码去加载:
MyClassLoader classLoader = new MyClassLoader();
Class clazz = classLoader.loadClass("Demo.class");
Object o=clazz.newInstance();
Method method = clazz.getMethod("say");
method.invoke(o);
输出:hello
遇到的OutOfMemory问题?如何处理
-
内存加载的数据量过大,一次性从数据库取太多数据
-
集合类中有对对象的引用,使用后未清除,GC不能进行回收
-
代码中存在循环产生过多重复的对象
-
启动参数堆内存值太小
JDK1.8以后PermSpace有哪些变化?MetaSpace大小默认是无限的吗
JDK1.8 以后使用元空间代替了PermSpace;字符串常量放到了堆内存中。
MeteSpace大小默认是没有限制的,一般根据系统内存大小,JVM会动态改变此值
MinorGC 和 FullGC在什么时候触发
Minor GC 在新生代内存满时
当永久代和老年代满时触发Full GC