标签:第三方 使用 ctime 两种 碰撞 lag 程序 targe 内存溢出
目录
内存溢出:
-xx:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m
拍照,设置程序初始化时内存栈大小,和程序最大的内存栈大小
Idea下载Memory Analyzer的插件来查看拍照得到的文件,看是哪个object占用的内存最多。
Java技术体系:Java程序设计语言, 个硬件平台上的Java虚拟机,Class文件格式,Java API, 第三方Java类库
线程独占区:虚拟机栈,本地方法栈,程序计数器
线程共享区:方法区,java堆
程序计数器:较小的内存空间,可看作当前线所执行的代码的行号指示器,便于CPU回到刚刚工作的地方。如果执行的是native方法,这个计数器的值为undefined。此区域唯一一个没有规定OutOfMemoryError的。
虚拟机栈:描述Java方法执行的动态内存模型。调用方法过多就会出现StackOverFlowError或OutOfMemory。
栈帧:每个方法执行都会创建一个,用来存储局部变量表,操作数栈,动态链接,方法出口等。
局部变量表:存放编译期可知的各种基本数据类型,引用类型,returnAddress类型。它们的内存空间(指引用,非实例所需内存)在编译期就完成了分配。当进入一个方法时,这个方法所需的栈帧的内存是固定的,运行期间不会改变局部变量表的大小。
本地方法栈:为native方法服务,其他和虚拟机栈类似。
方法区:存储虚拟机加载的类信息(类版本、字段、方法、接口)、常量、静态变量、即时编译器编译后的代码等数据。垃圾回收在方法区中的行为比较少。异常的定义。
String s1 = "a";
,这个“a”会存到常量池中,里面相当于一个HashSet。如果是String s2 = new String("a");
就会直接放到堆中,s2.intern() == s1
返回true。所以注意这两种方法产生的值,用==比较时的效果。java堆:空间最大,存放对象实例(不一定全部),垃圾收集器管理的主要区域。分有新生代(Eden、Survivor、Tenured Gen),老年代,永久代(java8前,但它是分在方法区中)。
直接内存:nio中的buffer对象所需内存,是堆外内存。
MetaSpace取代永久代解决永久代的溢出问题,但不属于堆内内存
new -> 根据new的参数在常量池中定位这个类的符号引用,如果没有找到,说明没有被加载,要进行加载和初始化 ->
加载,验证,准备,解析,初始化
public static int v = 8080; //初始值为0,加上final才是8080
<clinit>()
(不是平时说的自己编写构造器)。编译器自动收集类中的所有static的语句合并产生。当发现类的父类没有初始化,则先初始化父类。JVM会保证一个类的<clinit>()
线程安全。除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。
如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。
类初始化的情况:new;调用静态成员变量(非final)和方法;反射类;main方法所在的类,作为父类被初始化。
不会触发初始化情况:通过数组定义类引用;直接调用常量;自类访问父类的static域,自类不会被初始化。
//下面代码,先按顺序加载Test中的static语句,并设置默认值。(准备阶段,还没赋值,如果static块放在public static前面,打印出Test.a为0)先执行Test.main再加载A类。
public class Test {
public static int a = 100;
static {
System.out.println("initialize Test");
System.out.println(Test.a);
}
public static void main(String[] args) {
System.out.println("executing main");
System.out.println(A.MAX); //调用final变量属于被动引用,不会初始化A
A a = new A();
System.out.println(A.width);
}
}
class A {
public static int width = 100;
public static final int MAX = 100;
static {
System.out.println("initialize A");
}
public A() {
System.out.println("creating A");
}
}
为对象分配堆内存:指针碰撞,空闲列表。用哪种取决于垃圾回收的策略。
线程安全:线程同步,本地线程分配缓冲。前者性能低,后者为每个线程分配一份内存,当内存满了才通过线程同步来扩容。
对象结构:
对象访问定位:句柄,直接(HotSpot)。前者:栈通过线程共享区确定对象实例数据的位置(java堆)和对象类型数据的位置(方法区),这样即使GC,栈中的定位也不需要改变,但每次访问句柄也会影响效率。后者直接定位上述两个数据的位置。
垃圾的判断
引用计数法(基本不用):引用一次+1,引用为null时-1,而达到0时被回收。无法解决内部(堆内)引用非0,而外部为0的回收。
可达性分析法:对象包括虚拟机栈,方法区的类属性、常量所引用的对象,本地方法栈
回收方式
-XX:+UseSerialGC
控制吞吐率。SerialOld用于老年代。并行:垃圾收集线程的并行,用户线程是等待的。
并发:用户线程和垃圾回收线程同步执行(不一定并行,可能交替),收集时不会停顿用户线程。
内存分配策略
优先分配到YoungGen的eden,接下来空间不够,促发partial GC,如果survivor不够大放下旧的数据,旧的会直接到OldGen。
大对象直接老年代。
长期存活的老年代。
只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。
逃逸分析和栈上分配。当方法体内部引用了外部的变量,就会逃逸。
young区复制算法,Old区用标记清楚或者标记整理
GC促发条件
针对HotSpot VM的实现,它里面的GC其实准确分类只有两大种:
Partial GC:并不收集整个GC堆的模式
Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。
最简单的分代式GC策略,按HotSpot VM的serial GC的实现来看,触发条件是:
HotSpot VM里其它非并发GC的触发条件复杂一些,不过大致的原理与上面说的其实一样。(具体参考Major GC和Full GC的区别是什么?触发条件呢? - RednaxelaFX 的回答)
jps -mlv
:m参数,l全名,v虚拟机参数
jstat -gcutil xxxx 1000 10
:每1s查xxxx的GC统计信息一次,查10次。-class/ -compiler等options
jinfo -flag UseG1GC xxxx
:实时查看和调整虚拟机的各项参数。查看xxxx是否使用UseG1GC参数。
jmap -dump:format=b,file=path xxxx
:拍照。分析一般看histogram和dominator_tree
jstack
:打印线程信息。
Java Heap space区可大致分为Eden,survival1,survival2,老年区等。当Eden区满后会促发minor GC,存活的放到survival1,第二次满就将Eden和survival1存活的存到survival2,重复,多次存活放到老年区,老年区满了会促发GC。但如果某次存放到survival时满了,数据会直接到老年区,使得老年区更快满载而频繁GC。
-xx:+HeapDumpOnOutOfMemoryError //拍照 -XX:HeapDumpPath= //照片保存路径
-Xms20M //设置程序初始化时堆的大小
-Xmx20M //程序最大的堆大小
-Xmn10M //新生代10m
-verbose:gc -xx:+PrintGCDetail
-XX:MaxGCPauseMillis //收集器停顿时间
-XX:GCTimeRatio //吞吐量大小,0~100
-XX:SurvivorRatio=8 //8表示eden占80%,总量为10?
-XX:NewRatio=4 //4表示老年代占80%,总量为5?
-XX:PretenureSizeThreshold=6M //6M作为大对象,直接放入老年代
-XX:MaxTenuringThreshold=10 //存活10次移入老年代
-XX:TargetSurvivorRatio //垃圾回收存活比例,大于这个比例中的平均次数和上面的值的最小值就会成为老年代
-XX:+HandlePromotionFailure //+为开启,-为关闭。但这个参数在jdk 6好像已经不再使用。
-XX:+PrintFlagsFinal:打印jvm所有最终参数,:=表示修改过的。
标签:第三方 使用 ctime 两种 碰撞 lag 程序 targe 内存溢出
原文地址:https://www.cnblogs.com/code2one/p/9872383.html