JVM方法区&堆&栈

JVM方法区&堆&栈

方法区

运行时数据的存储区域,是可以进行线程资源共享。

元空间与永久代

方法区是用来存储:类信息,常量,字符串、静态变量、符号引用、方法代码。。。。。。
在这里插入图片描述

永久代和元空间都是对方法区的落地实现。

元空间永久代
时代JDK1.8开始JDK1.7及之前
位置内存中JVM中堆空间
空间逻辑存在,实际在内存中占用jvm空间
错误OutOfMemoryError:PermGenOutOfMemoryError:PermGen

栈(FILO 先进后出)

用来管理程序运行,程序的是指就是一个栈。

对象的引用才在栈中,实际的引用对象存在堆中,用栈的引用只想堆中。

栈的优势:存取数据快,不能数据共享

当栈满了在进行压栈就会报栈溢出异常(StackOverFlow)

栈存的是对象的引用,我们一直递归new对象就会出现异常。

public void a(){
    a();
}

就会报出栈满了的异常。

栈帧

用来描述栈里面的各个数据的“样子”。

栈针里面存储:调用方法、参数、本地变量、父帧、子帧。。。。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QiU4ZBC2-1584066711004)(image\image-20200313011523106.png)]

栈里的引用对象指向堆中的实际对象,实际对象包括方法区的对象类型数据。

jvm类型

  • SUN 公司 HotSpot
  • BEA 公司 JRockit
  • IBM 公司 J9VM

Java7之前:

Heap 堆,一个JVM实例中只存在一个堆,堆的内存大小是可以调节的。

可以存的内容:类、方法、常量、保存了类型引用的真实信息;

结构:

  • 新生区:Young (Eden-s0-s1)
  • 养老区:Old Tenure
  • 永久区:Perm

堆内存在逻辑上分为三个部分:新生、养老、永久(JDK1.8以后,叫元空间)

物理上只有 新生、养老;元空间在本地内存中,不在JVM中!

GC 垃圾回收主要是在 新生区和养老区,又分为 普通的GC 和 Full GC,如果堆满了,就会爆出 OutOfMemory;

新生区

新生区是和用户交互最多的,所有的对象都是在新生区诞生。

新生区的结构:伊甸园区(Eden)、幸存0区(s0)、幸存1区(s1)

新生区的GC:fullGC和普通GC(轻GC)

养老区

15次都幸存下来的对象进入养老区,养老区满了之后,触发 Full GC

默认是15次,可以修改!

永久区(Perm)

放一些 JDK 自身携带的 Class、Interface的元数据;

几乎不会被垃圾回收的;

OutOfMemoryError:PermGen 在项目启动的时候永久代不够用了?加载大量的第三方包!

JDK1.6之前: 有永久代、常量池在方法区;

JDK1.7:有永久代、但是开始尝试去永久代,常量池在堆中;

JDK1.8 之后:永久代没有了,取而代之的是元空间;常量池在元空间中;

过程:

  1. 所有的对象都是产生于伊甸园区,直到伊甸园区满了,调用轻GC对伊甸园区进行垃圾回收。剩下很少一部分(1%),进入幸存区。
  2. 当幸存区也满了的时候,触发一次FullGC,伊甸园区,幸存区都进行清理。
  3. 如果有对象可以活过一定数量(默认15次),则认为这个对象会经常使用,就会放入永久区(1.8以后叫元空间)
  4. 当元空间,幸存区,伊甸园区都满了,则会报出OOM错误

Sun HotSpot 虚拟机中,内存管理(分代管理机制:不同的区域使用不同的算法!)

内存调优

产看自己jvm的内存使用(作者电脑内存8G)

 public static void main(String[] args) {
        // 剩余剩余大小
        long freeMemory = Runtime.getRuntime().freeMemory();
        // jvm默认的内存大小
        long totalMemory = Runtime.getRuntime().totalMemory();
        // jvm最大内存大小
        long maxMemory = Runtime.getRuntime().maxMemory();

        //生成一个1MB的字节数组
        byte[] b = new byte[1024*1024];
        long totalMemory2 = Runtime.getRuntime().totalMemory();
        long maxMemory2 = Runtime.getRuntime().maxMemory();
        long freeMemory2 = Runtime.getRuntime().freeMemory();

        System.out.println(totalMemory2 - totalMemory);
        System.out.println(maxMemory2 - maxMemory);
        System.out.println("使用了:"+(freeMemory - freeMemory2)/1024/(double)1024+"MB");
        System.out.println("===================");
        System.out.println("jvm默认的内存大小:"+totalMemory/1024/(double)1024+"MB");
        System.out.println("jvm最大内存大小"+maxMemory/1024/(double)1024+"MB");
        System.out.println("剩余剩余大小"+freeMemory/1024/(double)1024+"MB");
    }

结果是:

0
0
使用了:1.0MB
===================
jvm默认的内存大小:121.0MB
jvm最大内存大小1787.0MB
剩余剩余大小117.125MB

我们发现totalMemory和maxMemory没有变化,生成一个字节数组只有freeMemory变化了。

其中maxMemory差不多是内存的1/4,totalMemory差不多为内存的1/64。都是在程序启动前定好的。

我们可以通过一下指令进行改变:

// -Xmx: 最大分配内存; 1/4  --->maxMemory -Xmx: 100m
// -Xms: 初始分配的内存大小; 1/64 --->totalMemory
-Xmx256m -Xms64M  (M 不区分大小写) 最大内存256 初始内存64

结果是:

jvm默认的内存大小:61.5MB
jvm最大内存大小228.0MB
剩余剩余大小58.20703125MB

我们再打印一下垃圾回收的信息

 PSYoungGen      total 18944K, used 4723K [0x00000000fd580000, 
                                           ....
 ParOldGen       total 44032K, used 0K [0x00000000f8000000
  object space 44032K, 0% used [0x00000000f8000000,0x00000000f8000000,0x00000000fab00000)
 Metaspace       used 3283K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 353K, capacity 388K, committed 512K, reserved 1048576K

PSYoungGen+ParOldGen=恰好等于61.5MB。即养老区和年轻代加起来刚好等于默认大小。

就证明元空间并不在jvm内

在这里插入图片描述

我们看看刚才说的OOM,当默认满了就会造成堆溢出

/**
 * -Xmx8m -Xms4m -XX:+PrintGCDetails
 */
public class Demo05 {
    public static void main(String[] args) {
        String str = " hello world";
        while(true){
            str += str + new Random().nextInt(Integer.parseInt("1999999999"));
        }
    }
}

在这里插入图片描述
一开始主要是普通GC,直到年轻代满了进行了一次清理,后面养老区满了进行了一次fullGC。直到最后一次:年轻代,养老区再也没有空间了,产生了OOM异常。

堆内存快照抓取

查看内存使用的情况。

我们可以用jdk自带的工具jconsole

E:\java1.8\bin\jconsole

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试代码:

 public static void main(String[] args) throws InterruptedException {
        int num = 0;
        while(true){
            num++;
            TimeUnit.SECONDS.sleep(1);
            num--;
        }
    }

还可以用idea的插件Jprofiler插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值