文章目录
- 前言
- 一、🚘 本地方法
- 1、什么是本地方法
- 2、为什么使用 Native Method
- 二、🚄 本地方法栈
- 三、🚥 堆内存
- 1、-Xms10m -Xmx10m 设置堆内存的大小
- 2、内存细分
- 3、设置堆空间的大小与OOM
- jps 查看Java程序进程
- jstat -gc 进程号 查看堆内存参数
- -XX:+PrintGCDetails 设置虚拟机参数,将堆内存情况打印到控制台
- OutOfMemoryError 内存溢出
- 4、年轻代与老年代
- 新生代与老年代的参数设置(一般不会调)
- -XX:-UseAdaptiveSizePolicy 关闭年轻代中伊甸园区和幸存者区的自适应比例
- -XX:SurvivorRatio=8 设置年轻代中伊甸园区和幸存者区的内存比例
- -Xmn:设置新生代的空间的大小。 (一般不设置)
- 5、图解对象分配过程
- 6、对象分配的特殊情况
- 7、常用的调优工具
- 四、🚒 GC垃圾回收
- 1、MinorGC / MajorGC / FullGC 的对比
- 2、测试垃圾回收
- 五、堆空间的其他细节及总结
- 1、堆空间的分代思想
- 2、总结内存分配策略
- 3、为对象分配内存 TLAB (伊甸园区)
- 4、小结堆空间的参数设置
- 5、堆是分配对象的唯一选择吗 ?
- 逃逸分析
- 栈上分配
- 同步策略
- 标量替换
前言
一、🚘 本地方法
1、什么是本地方法
package com.atguigu.java;
/**
* @author shkstart
* @create 2020 下午 8:53
*/
public class IHaveNatives {
// 被 native 关键字修饰的就是本地方法,有方法体,但是不是Java代码实现的,
//native 关键字不能 和 abstract 关键字一起使用
public native void Native1(int x);
public native static long Native2();
private native synchronized float Native3(Object o);
native void Native4(int[] ary) throws Exception;
}
2、为什么使用 Native Method
二、🚄 本地方法栈
三、🚥 堆内存
1、-Xms10m -Xmx10m 设置堆内存的大小
测试 将下面两个类的堆空间大小分别设置 10mb 和 20 mb
package com.atguigu.java;
/**
* -Xms10m -Xmx10m
*
* @author shkstart shkstart@126.com
* @create 2020 16:41
*/
public class HeapDemo {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
package com.atguigu.java;
/**
* -Xms20m -Xmx20m
* @author shkstart shkstart@126.com
* @create 2020 16:42
*/
public class HeapDemo1 {
public static void main(String[] args) {
System.out.println("start...");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
把上面两个代码跑起来,然后去JDK的安装目录找到,虚拟机监控运行起来。
2、内存细分
堆内存从逻辑上划分是三部分,年轻代,老年代,元空间。但是实际上元空间更偏向于方法区,所以堆空间我们还是看年轻代和老年代两部分。
3、设置堆空间的大小与OOM
package com.atguigu.java;
/**
* 1. 设置堆空间大小的参数
* -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
* -X 是jvm的运行参数
* ms 是memory start
* -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
*
* 2. 默认堆空间的大小
* 初始内存大小:物理电脑内存大小 / 64
* 最大内存大小:物理电脑内存大小 / 4
* 3. 手动设置:-Xms600m -Xmx600m
* 开发中建议将初始堆内存和最大的堆内存设置成相同的值。
*
* 4. 查看设置的参数:方式一: jps / jstat -gc 进程id
* 方式二:-XX:+PrintGCDetails
* @author shkstart shkstart@126.com
* @create 2020 20:15
*/
public class HeapSpaceInitial {
public static void main(String[] args) {
//返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
//返回Java虚拟机试图使用的最大堆内存量
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms : " + initialMemory + "M");
System.out.println("-Xmx : " + maxMemory + "M");
// System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");
// System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
jps 查看Java程序进程
jstat -gc 进程号 查看堆内存参数
-XX:+PrintGCDetails 设置虚拟机参数,将堆内存情况打印到控制台
OutOfMemoryError 内存溢出
设置堆大小
执行程序
package com.atguigu.java;
import java.util.ArrayList;
import java.util.Random;
/**
* -Xms600m -Xmx600m
* @author shkstart shkstart@126.com
* @create 2020 21:12
*/
public class OOMTest {
public static void main(String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while(true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(new Picture(new Random().nextInt(1024 * 1024)));
}
}
}
class Picture{
private byte[] pixels;
public Picture(int length) {
this.pixels = new byte[length];
}
}
堆内存拉满
4、年轻代与老年代
新生代与老年代的参数设置(一般不会调)
-XX:-UseAdaptiveSizePolicy 关闭年轻代中伊甸园区和幸存者区的自适应比例
-XX:SurvivorRatio=8 设置年轻代中伊甸园区和幸存者区的内存比例
-Xmn:设置新生代的空间的大小。 (一般不设置)
5、图解对象分配过程
伊甸园区满了会进行垃圾回收,幸存者放入 01 区,当幸存者过多时,01 区装不下仍然不会进行垃圾回收,而是直接让多出的幸存者进入老年代。
6、对象分配的特殊情况
7、常用的调优工具
四、🚒 GC垃圾回收
1、MinorGC / MajorGC / FullGC 的对比
对于程序的调优,我们更多关注的是尽量减少 垃圾回收的次数,因为每次垃圾回都会让用户线程停止工作。
2、测试垃圾回收
字符串常量池以前在方法区,现在在堆空间
package com.atguigu.java1;
import java.util.ArrayList;
import java.util.List;
/**
* 测试MinorGC 、 MajorGC、FullGC
* -Xms9m -Xmx9m -XX:+PrintGCDetails
* @author shkstart shkstart@126.com
* @create 2020 14:19
*/
public class GCTest {
public static void main(String[] args) {
int i = 0;
try {
List<String> list = new ArrayList<>();
String a = "atguigu.com";
while (true) {
list.add(a);
a = a + a;
i++;
}
} catch (Throwable t) {
t.printStackTrace();
System.out.println("遍历次数为:" + i);
}
}
}
五、堆空间的其他细节及总结
1、堆空间的分代思想
2、总结内存分配策略
package com.atguigu.java1;
/** 测试:大对象直接进入老年代
* -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
* @author shkstart shkstart@126.com
* @create 2020 21:48
*/
public class YoungOldAreaTest {
public static void main(String[] args) {
byte[] buffer = new byte[1024 * 1024 * 20];//20m
}
}
3、为对象分配内存 TLAB (伊甸园区)
4、小结堆空间的参数设置
5、堆是分配对象的唯一选择吗 ?
逃逸分析
package com.atguigu.java2;
/**
* 逃逸分析
*
* 如何快速的判断是否发生了逃逸分析,大家就看new的对象实体是否有可能在方法外被调用。
* @author shkstart
* @create 2020 下午 4:00
*/
public class EscapeAnalysis {
public EscapeAnalysis obj;
/*
方法返回EscapeAnalysis对象,发生逃逸
*/
public EscapeAnalysis getInstance(){
return obj == null? new EscapeAnalysis() : obj;
}
/*
为成员属性赋值,发生逃逸
*/
public void setObj(){
this.obj = new EscapeAnalysis();
}
//思考:如果当前的obj引用声明为static的?仍然会发生逃逸。
/*
对象的作用域仅在当前方法中有效,没有发生逃逸
*/
public void useEscapeAnalysis(){
EscapeAnalysis e = new EscapeAnalysis();
}
/*
引用成员变量的值,发生逃逸
*/
public void useEscapeAnalysis1(){
EscapeAnalysis e = getInstance();
//getInstance().xxx()同样会发生逃逸
}
}
栈上分配
package com.atguigu.java2;
/**
* 栈上分配测试
* -Xmx1G -Xms1G -XX:+PrintGCDetails -XX:-DoEscapeAnalysis(关闭逃逸分析) -XX:-DoEscapeAnalysis(开启逃逸分析)
* @author shkstart shkstart@126.com
* @create 2020 10:31
*/
public class StackAllocation {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
alloc();
}
// 查看执行时间
long end = System.currentTimeMillis();
System.out.println("花费的时间为: " + (end - start) + " ms");
// 为了方便查看堆内存中对象个数,线程sleep
try {
Thread.sleep(1000000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
private static void alloc() {
User user = new User();//未发生逃逸
}
static class User {
}
}
同步策略
package com.atguigu.java2;
/**
* 同步省略说明
* @author shkstart shkstart@126.com
* @create 2020 11:07
*/
public class SynchronizedTest {
public void f() {
Object hollis = new Object();
synchronized(hollis) {
System.out.println(hollis);
}
}
}
标量替换
package com.atguigu.java2;
/**
* 标量替换测试
* -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
* @author shkstart shkstart@126.com
* @create 2020 12:01
*/
public class ScalarReplace {
public static class User {
public int id;
public String name;
}
public static void alloc() {
User u = new User();//未发生逃逸
u.id = 5;
u.name = "www.atguigu.com";
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
alloc();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为: " + (end - start) + " ms");
}
}
/*
class Customer{
String name;
int id;
Account acct;
}
class Account{
double balance;
}
*/