知识点整理:
- java运行时数据区域(内存模型)
- 堆
- Enden、from survivor、to survivor
- 老年代:大对象、15轮、相同多
- 存放实例、实例变量、数组
- 方法区
- 类信息、常量池、静态变量
- 栈 私有
- 栈帧:
- 局部变量表:局部变量---基本数据、对象引用、返回地址类型
- 操作数栈:算数运行、递归时的参数传递
- 动态链接:指向常量池中的符号引用,运行期间转化为直接引用
- 方法返回地址:
- return正常出口---恢复上一个栈帧数据、返回值入操作数栈、调用程序计数器
- 异常出口:异常处理器
- 程序计数器:字节码指令的行号指示器、私有
- OOM:
- Java heap spacess:内存泄露【垃圾对象太多】、内存溢出
- PermGen space:类信息过多、常量过多
- stackOverflowerError:递归深度、死循环
- GC 可达性分析:栈、方法区静态、常量池引用对象
- gc机制:分代收集:新生代--复制、老年代--标记整理
- 垃圾收集器:Parallel Scavenge(old)吞吐量、CMS服务响应速度、G1
- gc触发:minor、full触发(空间分配担保)
- gc过程:引用链OopMap、安全点、安全区
- 对象内存 对象头、实例数据、对齐
- 访问定位:句柄、直接指针
- new对象过程
- 类加载过程
- 类加载器:双亲委派模型
- 触发时机:new、静态、反射、先加载父类
jvm的内存布局
JVM内存结构主要有三大块:堆内存、方法区、栈
- 堆内存是JVM中最大的一块,由年轻代和老年代组成,线程共享
-
- 年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;
- 方法区存储类信息、常量池、静态变量等数据,线程共享
- 栈分为java虚拟机栈、本地方法栈,主要用于方法的执行,线程私有
为什么两个幸存空间 From Survivor、To Survivor 解决碎片问题
引进了“幸存区”作用:
- 降低gc频率,如果活着的对象全部进入老年代,老年代很快被填满,Full GC 的频率大大增加
- 解决碎片问题
- Eden 空间快满时 Minor GC ,频率得以降低
缺点:两个 Survivor ,10%的空间浪费、复制对象开销
机制
- 如果对象在新生代 gc 之后任然存活,暂时进入幸存区;以后每过一次 gc ,对象年龄+1,直到某个设定的值15或直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中
- 把 Eden : From Survivor : To Survivor 空间大小设成 8 : 1 : 1 ,对象总是在 Eden 区出生, From Survivor 保存当前的幸存对象, To Survivor 为空。一次 gc 发生后:
- Eden 区活着的对象 + From Survivor 存储的对象被复制到 To Survivor ;
- 清空 Eden 和 From Survivor ;
- 颠倒 From Survivor 和 To Survivor 的逻辑关系: From 变 To , To 变 From
介绍jvm每个区域的作用 堆、方法区、栈、程序计数器
Java堆(Heap)
- 虚拟机启动时创建,内存中最大的一块,线程共享
- 存放对象实例、数组、实例成员变量
- 垃圾收集器管理的主要区域,也被称做“GC堆”,逻辑上连续,一般采用分代收集算法
方法区(Method Area)
- 线程共享,存储已被虚拟机加载的,类加载器 加载的---
- 类信息:
- 常量池:静态常量池:编译期生成的字面量【final常量】和符号引用 ;运行时常量池:jvm虚拟机在完成类装载操作后,运行期间生成的新常量
- 静态变量、编译器编译后的静态变量数据
- 即时编译器编译后的代码
- 内存回收目标主要是常量池的回收和对类型的卸载
常量池的好处
常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中
- 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间
- 节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等
JVM栈(JVM Stacks)
Java虚拟机栈
- 线程私有,生命周期与线程相同
- 描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接(指向常量池)、方法返回地址等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
- 局部变量表:局部变量:方法参数和方法内部定义的变量,局部变量表所需的内存空间在编译期间完成分配;【随着方法的消失而消失】
- 编译期可知的基本数据类型(boolean、byte、char、short、int、float、long、double)
- 对象or数组引用(作为参数)(reference类型 指针指向,内存数据存在堆中,类型信息存储在方法区)
- 返回地址类型(指向了一条字节码指令的地址)
- 操作数栈:先入后出,初始为空,在方法的执行过程中加入数据: 算术运算、方法参数、方法返回值
- 动态连接:栈帧包含的一个指针:指向运行时常量池中,所属方法的符号引用,在运行期间将符号引用转化为直接引用称为动态链接
- 方法返回地址 两种方式退出:
- 正常完成出口:是执行引擎遇到一个return的字节码指令,恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈中,然后调用程序计数器执行后一条指令。
- 异常完成出口:方法执行过程中遇到了异常,异常没有在方法体内得到处理,返回地址由异常处理器确定
本地方法栈(Native Method Stacks)
本地方法栈则是为虚拟机使用到的Native本地方法服务
程序计数器(Program Counter Register)
- 线程私有,一块较小的内存空间
- 当前线程所执行的字节码指令的行号指示器
- 通过改变这个计数器的值来选取下一条字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
- 每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响
可达性分析+分代收集+回收算法
可达性分析=引用链枚举 判断一个对象是否存活、GC对象的判定方法
判断一个对象是否存活有两种方法:
1.引用计数法 xxx
对象设置引用计数器,引用对象时,计数器加一;引用失效时,计数器就减一;为零时,垃圾回收
缺陷:无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,引用计数器都不为零,无法垃圾回收
2.可达性算法(引用链法) 栈、方法区的引用对象
从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有引用链相连时,则说明此对象不可用
- 虚拟机栈中引用的对象
- 本地方法栈JNI引用的对象
- 方法区静态变量引用的对象
- 方法区常量池引用的对象
当一个对象不可达GC Root时,这个对象并不会立马被回收,被真正的回收需要经历两次标记
可达性分析中没有与GC Root的引用链,此时第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法
若有必要执行finalize()方法,对象将会放在F-Queue队列中,虚拟机会触发一个Finalize()线程去执行,GC对处于F-Queue中的对象进行第二次被标记,这时,该对象将被移除”即将回收”集合,等待回收
HotSpot 基于安全点的引用链枚举 (GC)
- 准确式GC 快速完成GC Roots引用链枚举
- 引用链枚举时,应用OopMap数据结构,快速完成GC Roots引用链枚举
- OopMap:在类加载完成时,存储 :寄存器和栈的 <偏移量,数据及数据类型>;
- 安全点检测:程序有长时间执行特征 --- 方法调用、循环跳转、异常跳转时
- 仅需在安全点记录OopMap信息;每条指令生成OopMap,则空间成本太大;
- 程序执行时仅在安全点停下来GC
- 多线程的主动式中断,使得各个线程都跑到安全点再停顿
- 在安全点、创建的对象分配内存时 设置一个标志
- 各个线程执行时主动轮询该标志,若为真,则中断挂起
- 安全区域检测:代码中,引用关系不发生变化
- 线程没有分配CPU时间,无法跑到安全点,导致代码中,引用关系不发生变化
- GC时可忽略该标识自己处于安全区域的线程
- 要离开安全区域,需要收到系统已完成引用链枚举的信号
java垃圾回收机制
- 自动:不需要显示释放对象内存,虚拟机自行执行
- GC时间:在虚拟机空闲、堆内存不足时触发,低优先级垃圾回收线程
- GC对象:没任何引用的对象(可达性算法)
- 方式:minorGC fullgc
java中垃圾回收的方法有哪些
- 标记-清除:x
- 标记哪些要被回收的对象,然后统一回收
- 特点:
-
-
- 标记和清除的效率低;
- 产生大量不连续的内存碎片,GC频繁---以后程序在分配较大的对象时,由于没有充足的连续内存导致GC频繁
- 复制算法: 适合年轻代
-
- 把 Eden : From Survivor : To Survivor 空间大小设成 8 : 1 : 1 ,对象总是在 Eden 区出生,若Eden区满,触发minor GC
- 若GC后,存活的对象太多,to survivor内存不够时,通过分配担保机制复制到老年代;老年代 full gc
- 标记-整理 适合老年代
-
- 在清除对象的时候先将可回收对象移动到一端,然后清除掉端边界以外的对象
- 解决大量内存碎片问题
- 当对象存活率较高时,效率也不差
- 分代收集 综合
-
- 根据对象的生存周期,将堆分为新生代和老年代
- 在新生代中,对象生存期短,采用复制算法
- 老年代里的对象存活率较高,使用标记-整理 或者 标记-清除
5.G1收集器的可预测停顿
Jvm对象分配方案 分代机制 何时进入老年代
对象优先在Eden分配;Eden区没有足够空间时,发起一次MinorGC;
- 大对象直接进入老年代;
通过 -XX:PretenureSizeThreshold参数设置;
- 长期存活的对象进入老年代:存活轮次多 15
MinorGC一次还存活在Survivor中,年龄+1; 通过 -XX:MaxTenuringThreshold参数设置;
- 年龄相同的大于一半,动态对象年龄判别:
在survivor中年龄相同的所有对象大小总和大于 survivor 一半,年龄大于或等于该年龄的对象进入老年代;防止survivor满
- 空间(检测)分配担保机制:fullgc前 需要比较两次
每次MinorGC之前,会检查老年代最大连续可用空间是否大于 新生代所有对象的总空间,大于则表示安全,MinorGC;
如果小于:(JDK 6U24不再检查HandlePromotionFailure,一定会冒险)
检查HandlePromotionFailure 为 true ,允许冒险,历次晋升到老年代的对象平均大小与本次进入的对象大小比较,若历次大 MinorGC,若小 FullGC;
HandlePromotionFailure 为 false,直接进行FullGC;
简述minor和fullgc,minorGC以及FullGC触发条件
- 新生代进行一次垃圾清理,被称为minorGC
- 老年代进行一次垃圾清理,被称为fullGC或者majorGC。速度一般较慢
- 大多数新生对象生命周期很短,所以MinorGC通常很频繁,回收速度也较快
- 降低MinorGC的频率、减少fullGC的次数
触发条件:
MinorGC:Eden区满时触发MinorGC;FullGC也会伴随有MinorGC;
FullGC:a. 老年代不够分配时,触发FullGC;
b. 空间(检测)分配担保机制:MinorGC触发前,比较老年代的剩余空间和新生代所有对象大小,老年代小,且不允许冒险,则fullgc
允许冒险,继续比较对象历次平均大小与本次进入老年代的大小,若本次大,则FullGC;
为什么要担保:每次minorgc,新生代年龄满15的会进入老年代,如果新生代全部对象都一起满15会导致老年代不够放
c. 手动调用System.gc(),提醒JVM FullGC,但不可控;
栈溢出、堆溢出、内存溢出的原因
栈溢出的原因
- 递归调用
- 大量循环、死循环
- 函数体内局部变量数组过大
内存溢出的原因是什么
- JVM没有及时回收,堆内存泄露
- 循环或递归中大量的new对象,堆溢出
- 静态变量大、类加载过多,类信息、方法区溢出
- 函数体内的数组过大,栈溢出
- 递归或无限递归,栈溢出
oom类型
导致的原因
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用
内存泄漏 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,最终会导致out of memory
1,Java Heap 溢出
堆 存放对象实例和数组,线程共享;
- 内存不够:实例过多、数组过大、大量的new对象;集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
- 内存泄露:静态集合类引起内存泄露;各种连接没有关闭;单例对象持有外部对象的引用
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess
先分清是内存泄漏还是内存溢出
内存泄漏
通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象与GC Roots相关联路径,
内存溢出
检查虚拟机的参数(-Xmx与-Xms)的设置是否适当 ;
通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,确认内存中的对象是否是必要的
2, 方法区溢出 运行时常量池溢出
常量池溢出;
加载到方法区的类过多;类信息过多:同一个类文件被不同的类加载器重复加载
用于存储已被JVM加载的类信息、常量池、静态变量等。编译器编译后的代码,线程共享
异常信息:java.lang.OutOfMemoryError:PermGen space
-XX:PermSize和-XX:MaxPermSize 设置方法区的大小
3, 虚拟机栈和本地方法栈溢出
递归太深、死循环导致栈帧创建过多
线程请求的栈深度大于虚拟机允许的最大深度,抛出StackOverflowError异常
在扩展栈时无法申请到足够的内存,则抛出OutOfMemoryError异常
栈的大小越大,可分配的线程数就越少
4, 本机直接内存溢出
java.lang.OutOfMemoryError
-XX:MaxDirectMemorySize指定,默认和Java堆最大值一样
垃圾收集器 3类
1.吞吐量优先
Parallel Scavenge[ˈskævɪndʒ] 新生代 复制算法
parallel Old 老年代 标记-整理
2.重视服务响应速度,最短回收停顿时间
Parallel Scavenge[ˈskævɪndʒ] 新生代 复制算法
CMS(Concurrent Mark Sweep) 老年代 并发的标记-清除
- 初始标记(CMS initial mark)S
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark) S
- 并发清除(CMS concurrent sweep)
初始标记、重新标记:这两个步骤仍然需要“Stop The World”【所有线程停止工作】
- 初始标记仅仅只是标记一下GC Roots能直接关联到的对象【栈、方法区】,速度很快,要“Stop The World”
- 并发标记阶段就是进行GC Roots Tracing【引用链跟踪过程】的过程,刚才产生的集合中标记出存活对象
- 重新标记阶段则是为了修正并发标记期间,因用户程序继续运作,而导致标记产生变动的 部分对象的标记记录,要”Stop The World“
- 比初始标记阶段稍长一些,但远比并发标记的时间短
- 并发清理回收所有的垃圾对象,由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作
缺点:
- CPU资源敏感(需要并发)
- 无法处理浮动垃圾(边并发GC边产生垃圾)
- 在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活
- 要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败【运行时模式错误】
- 大量空间碎片(需要碎片整理)
- CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费附送一个碎片整理过程
3.面向服务器端应用,低停顿
G1收集器
- 并发,边GC边执行代码
- 分代收集:单个收集器,即可管理不同存活时间的对象
- 并发标记-整理算法,无空间碎片
- 可预测的停顿:可设定在长度为M毫秒内,垃圾收集时间不超过N毫秒
- 实现方式:将堆划分为相等的region,设置一个价值标记,进行跟踪;优先回收价值最大的region
- 价值标记:回收所获得的空间大小和所需时间的经验值,用优先队列维护
初始标记 GC Roots能直接关联到的对象 Stop the world
并发标记 从GC root开始对堆中的对象进行可达性分析、找出存活对象
最终标记 修正在并发标记其间,用户程序继续运行产生变动的标记记录 S
筛选回收 对各个region回收价值和成本进行排序,根据用户期望停顿时间进行回收 【S,或者并发回收】
类加载器
通过类的全限定名获取该类的二进制字节流
主要有一下四种类加载器:
1. 启动类加载器(Bootstrap ClassLoader):加载Java核心类库[JAVA_HOME/lib];无法被直接饮用,若自定义类加载器时需要委派给启动类加载器,则直接使用null代替
2. 扩展类加载器(extensions class loader): 加载Java 的扩展库[JAVA_HOME/lib/ext];发者可直接使用扩展类加载器
3. 系统类加载器(system class loader):根据类路径(CLASSPATH)加载 Java 应用的类。ClassLoader.getSystemClassLoader()来获取
4. 用户自定义类加载器,继承 java.lang.ClassLoader类的方式实现
类加载器的双亲委派模型
层次关系:启动类加载器--扩展类加载器---系统类加载器---自定义类加载器
- 某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归;若父类加载不了,再返回由子类加载
- 优点:
- 使类加载器具有带优先级的层次关系;
- 保证java程序稳定运行
- 父子关系采用组合而非继承的方式实现
OSGI破坏双亲委派模型
- 实现代码热替换、模块热部署,想电脑插拔鼠键一样
- OSGI实现自定义类加载机制,每一个程序模块Bundle都有自己的类加载器,更换Bundle时连同类加载器一起替换
- 类加载模型为复杂的网络结构,当收到类加载请求时,OSGI根据网络结构,实现自己的类搜索顺序
类加载过程 【加载、验证、准备、解析、初始化】
1.加载 3
- 通过一个类的全限定名来获取定义此类的二进制字节流(Class文件、网络、动态生成、数据库等);
- 转化为方法区的运行时数据结构;【方法区没有所以才加载类、有了就拷贝】
连接阶段包括:验证、准备、解析
2.验证 信息
验证字节流中的信息,符合当前虚拟机要求,不危害虚拟机安全
4个阶段的检验动作:
- 文件格式验证:字节流是否符合Class文件格式规范
- 元数据验证:语义分析,保证字节流描述的信息符合Java语言规范
- 字节码验证:数据流和控制流分析,确定程序语义是合法的
- 符号引用验证:确保解析动作能正确执行
3.准备 分配初值 方法区中
- 方法区中为静态变量分配内存,并设置初始 0 值
- 为final常量赋 初始值(程序定义的值)
不包括实例变量,实例变量在对象实例化时随着对象一起分配在堆中
例:初始值“通常情况”下是数据类型的零值,假设一个类变量的定义为:
1 |
public static int value=123; |
那变量value在准备阶段过后的初始值为0而不是123.把value赋值为123的动作将在初始化阶段才会执行。
特殊情况:public static final int value=123,final修饰的常量,始化为123
4.解析 符号引用->直接引用
- 虚拟机将方法区【常量池内】【符号引用】 -替换为- 【直接引用】;多态实现
- 符号引用以一组 符号来描述所引用的目标;
- 直接引用可以是 直接指向目标的指针
解析动作主要针对类、接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
5.初始化 静态变量、静态代码块、方法区存储类信息
- 为静态变量赋程序中的值
- 执行static代码块
- static代码块只有jvm能够调用
- 多线程仅允许一个线程初始化操作,其余线程等待
- 类的加载顺序是先加载父类后加载子类
- 方法区会存储当前类信息:
- 包括类的静态变量、实例变量定义、父类的类信息引用
- 类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)
- 实例初始化代码(定义实例变量时的赋值语句:实例方法、构造方法)
6.接new过程的堆中分配内存
触发条件
(1) 创建new类实例 (2) 访问静态变量、对静态变量赋值 (3) 调用静态方法 (4) 反射(如Class.forName(“com.shengsiyuan.Test”)) (5) 加载子类前,先加载父类 (6) Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类
new一个对象创建的过程
包括5个步骤:类加载检查过程、堆分配内存、内存初始化为零、对象头设置、执行对象实例方法
- 类加载检查过程 检查是否能在常量池中定位到类的符号引用,无则执行类加载
- “new” 操作转换为Class文件中“new”字节码指令
- 检查指令参数是否能在常量池中定位到一个类的符号引用:
- 如果不能定位到,执行类加载过程;【见上面】
- 如果能定位到,则在堆中创建一个对象的拷贝
- 堆中分配内存 类加载后,Java堆分配内存
- 类加载完成后确定对象所需内存的大小,从Java堆分配
- 对象实例变量初始化,零值、null值
- 保证实例变量不赋初始值,也可以使用;
- 对象头设置
- 主要设置对象头信息,包括偏向锁模式、对象的哈希码、对象的GC分代年龄等(详见下节);
- 执行对象实例方法
- 实例变量赋值
- 构造方法
- memory = allocate(); //1:分配对象的内存空间
- ctorInstance(memory); //2:初始化对象
- instance = memory; //3:设置instance指向刚分配的内存地址
2.3步可能会被指令重排序优化,导致单例模式出错、加volatile
将方法区内的 将实例变量的定义 拷贝到堆,按照程序中定义的赋值
初始化顺序是先初始化父类再初始化子类,初始化时先执行静态代码块,然后是构造方法
String s = new String(“xyz”);产生几个对象
先去方法区常量池中查找是否已经有了”s”对象
- 如果没有则在常量池中创建一个“ s ”对象,然后堆中再创建一个常量池中此”s”对象的拷贝对象;2个对象
- 否则,只需要在堆中创建一个“ s ”对象
自定义类加载器
需要继承java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,即指明如何获取类的字节码流。
如果要符合双亲委派规范,则重写findClass方法(用户自定义类加载逻辑);要破坏的话,重写loadClass方法(双亲委派的具体逻辑实现)
对象的内存布局:对象头、实例数据、对齐填充
- 对象头信息
- Mark Word 6个: hash码、GC年龄、锁状态标识、持有的锁owner、偏向锁线程ID、偏向锁时间戳
- 类型指针 判断对象属于哪个类的实例,指向所属类的指针,
- 实例数据 存储真正有效数据
- 字段的分配策略:相同宽度的字段总是被分配到一起,便于之后取数据;
- 父类定义的变量会出现在子类前面
- 对齐填充 占位符的作用,非必须
对象实例数据的访问定位:
1.使用句柄地址:二次定位
java堆中将会划出内存作为句柄池,reference存储句柄地址,再通过句柄中的实例数据指针,查找堆中对象实例数据
优点:对象被移动时,只改变实例数据指针(垃圾回收时)
2.直接指针访问对象实例数据,reference中存储的就是对象堆中的实例数据地址【类型数据都是在方法区】
优点:只进行了一次指针定位,节省了时间,而这也是HotSpot采用的实现方式;由于对象访问比较频繁,这个较好
类型数据都是通过指针二次查找,存储在方法区
JVM优化 内存、收集器、工具
参数调优
java堆栈大小设置相关
-Xms :设置Java堆栈的初始化大小
-Xmx :设置最大的java堆大小
-XX:PermSize and MaxPermSize :设置持久带的大小
-XX:NewRatio :设置年轻代和老年代的比值
-XX:NewSize :设置年轻代的大小
打印垃圾回收信息
verbose:gc :记录GC运行以及运行时间,一般用来查看GC是否有瓶颈
-XX:+PrintGCDetails :记录GC运行时的详细数据信息,包括新生占用的内存大小及消耗时间
-XX:-PrintGCTimeStamps :打印收集的时间戳
垃圾集器配置
-XX:+UseParallelGC :使用并行垃圾收集器
-XX:ParallelGCThreads=N,设置并行垃圾回收的线程数,此值可以设置与机器处理机数量一致(有建议core+3/4);
-XX:-UseConcMarkSweepGC :使用CMS 并发标志扫描收集器
-XX:-UseSerialGC :使用串行垃圾收集器
配置日志文件
-Xloggc:filename :设置GC记录的文件
-XX:+UseGCLogFileRotation :启用GC日志文件的自动转储
-XX:GCLogFileSize=1M :控制GC日志文件的大小
类加载和跟踪类加载和卸载的信息【诊断内存泄露】
-XX:+TraceClassLoading :跟踪类加载的信息
-XX:+TraceClassUnloading :跟踪类卸载的信息
JVM性能调优工具 VisualVM、BTrace、Yslow
- jps(Java Virtual Machine Process Status Tool)
- 主要用来输出JVM中运行的进程状态信息
- jstack(Java Stack Trace)
- 主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:
- jmap(Java Memory Map)
- jmap用来查看堆内存使用状况,一般结合jhat使用
- jhat(Java Heap Analysis Tool)
- 用于对Java 堆进行离线分析的工具,它可以对不同虚拟机中导出的heap信息文件进行分析
- jstat(Java Virtual Machine Statistics Monitoring Tool)
- Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:
- jconsole
- 一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。命令行里打 jconsole,选则进程就可以了
- GC日志:内存回收的详细情况,频繁程度、GC导致应用停止的时间,GC的原因
java GC延迟问题 GC优化 原因及解决办法
- 详细的 GC 日志,查看 GC 日志中是否出现了上述的典型内存异常问题(promotion failed, concurrent mode failure)
- 借助 Linux 平台下的 iostat、vmstat、netstat、mpstat 等命令监控系统情况
- 使用 VisualVM、GCHisto 这个 GC 图形用户界面工具,可以统计出 Minor GC 及 Full GC 频率及时长分布,可参考:http://blog.csdn.net/wenniuwuren/article/details/50760259
- 如果程序没问题,参数调了几次还是不能解决,可能说明流量太大,需要加机器把压力分散到更多 JVM 上
性能测试:AJmeter
SpringMVC流程
账户A转到账户B,什么流程 rollback
脏读,不可重复读,幻读
介绍一下hadoop mapreduce能解决什么问题
hadoop 海量文档计算重复最多的词