JVM快速入门(三)——Native,栈内存,堆详解

Native情景引入

看一段线程代码

public class MyTest {
    public static void main(String[] args) {
       Thread t=new Mythread();
       t.start();


    }
}
    class  Mythread extends Thread{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"====>"+i);
            }
        }
    }

我们点击start方法查看源码 ,发现里面有个native关键字

Native关键字是什么?

native :凡是带了native 关键字的,说明java的作用范围达不到了,会去调用底层c语言的库!

会进入本地方法栈

调用本地方法接口(JNI Java Native Interface)

JNI:扩展java使用,融合不同的变成语言,c、c++  python等语言

Java诞生的时候C、C++横行,想要立足,必须要有调用c、C++的程序~

它在内存区域中专门开辟了一块标记区域: Native Method Stack,登记native方法

在最终执行的时候,通过JNI加载本地方法库中的方法

关于栈

栈:栈内存,主管程序的运行,生命周期和线程同步; 

程序运行,最先就是把main方法压入栈

线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题,—旦线程结束,栈就Over ! |

线程是最小的执行单位,他不具备记忆功能,他只负责去干,那这个记忆就由:程序计数器来记录

程序计数器是用在多线程的情况下,他会记录每个线程的执行位置,当cpu切换回来的时候,就可以找到之前的位置,继续执行 

栈存放:8大基本类型+对象引用+实例的方法

8大基本类型:int short long byte double float boolean char

栈对应的是线程,栈帧对应的线程里的方法

栈里有多个栈帧

子帧调用子程序,父帧回到原来程序的执行位置,并继续执行

内存和堆内存的区别:

1、栈内存用来存放基本类型的变量和引用变量,堆内存用来存储java中的对象,无论是成员变量,局部变量,还是类变量,他们指向的对象都存储在堆内存中。

2、栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属的线程中可见,即栈内存可以理解成线程的私有内存;堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。一个JVM只有一个堆内存

3、如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.stackoverflowerror 错误(SOF);如果是堆内存内没有可用的空间存储生成的对象,jvm会抛出java.lang.outofmemoryerror错误(OOM)。

4、栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈会很快充满。-Xss设置每个线程堆栈内存的大小 -Xms可以设置堆内存开始时的大小。

总结:

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
 

关于堆

首先我们要知道目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9。

JRockit和J9不存在永久代这种说法。这里只讨论HotSpot虚拟机,这也是目前使用的最多的JVM。

堆又可以分为三个区域

jvm 堆内存结构图 

JDK8之前

JDK8以后再堆中移除了永久代(区),把这个区域移到了本地内存中,即元空间!!

在HotSpot虚拟机中存在三种垃圾回收现象,minor GC、major GC和full GC。对新生代进行垃圾回收叫做minor GC,对老年代进行垃圾回收叫做major GC,同时对新生代、老年代和永久代进行垃圾回收叫做full GC。许多major GC是由minor GC触发的,所以很难将这两种垃圾回收区分开。major GC和full GC通常是等价的,收集整个GC堆。

年轻代

也叫新生代,顾名思义,主要是用来存放新生的对象。新生代又细分为 Eden区、SurvivorFrom区、SurvivorTo区。

新创建的对象都会被分配到Eden区(如果该对象占用内存非常大,则直接分配到老年代区), 当Eden区内存不够的时候就会触发MinorGC(Survivor满不会引发MinorGC,而是将对象移动到老年代中),

在Minor GC开始的时候,对象只会存在于Eden区和Survivor from区,Survivor to区是空的。

Minor GC操作后,Eden区如果仍然存活(判断的标准是被引用了,通过GC root进行可达性判断)的对象,将会被移到Survivor To区。而From区中,对象在Survivor区中每熬过一次Minor GC,年龄就会+1岁,当年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置,默认是15)的对象会被移动到年老代中,否则对象会被复制到“To”区。经过这次GC后,Eden区和From区已经被清空。

“From”区和“To”区互换角色,原Survivor To成为下一次GC时的Survivor From区, 总之,GC后,都会保证Survivor To区是空的。

奇怪为什么有 From和To,2块区域?
这就要说到新生代Minor GC的算法了:复制算法

把内存区域分为两块,每次使用一块,GC的时候把一块中的内容移动到另一块中,原始内存中的对象就可以被回收了,

优点是避免内存碎片

老年代

随着Minor GC的持续进行,老年代中对象也会持续增长,导致老年代的空间也会不够用,最终会执行Major GC(MajorGC 的速度比 Minor GC 慢很多很多,据说10倍左右)。Major GC使用的算法是:标记清除(回收)算法或者标记压缩算法

    标记清除(回收):1. 首先会从GC root进行遍历,把可达对象(存过的对象)打标记

                                    2. 再从GC root二次遍历,将没有被打上标记的对象清除掉。

        优点:老年代对象一般是比较稳定的,相比复制算法,不需要复制大量对象。之所以将所有对象扫描2次,看似比较消耗时间,其实不然,是节省了时间。举个栗子,数组 1,2,3,4,5,6。删除2,3,4,如果每次删除一个数字,那么5,6要移动3次,如果删除1次,那么5,6只需移动1次。

        缺点:这种方式需要中断其他线程(STW),相比复制算法,可能产生内存碎片。

     标记压缩:和标记清除算法基本相同,不同的就是,在清除完成之后,会把存活的对象向内存的一边进行压缩,这样就可以解决内存碎片问题。 


当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

永久代(元空间)

永久代又被人称为‘非堆’。方法区是抽象概念, 永久代和元空间都是对方法区的具体实现

 在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间,Metaspace)的区域所取代。

因此常量池平时存放在堆中,运行时的常量池存放在永久代(非堆)中。

这个区域常驻内存的。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息~,这个区域几乎不存在垃圾回收! 关闭VM虚拟就会释放这个区域的内存~


值得注意的是:元空间并不在虚拟机中,而是使用本地内存(之前,永久代是在jvm中)。这样,解决了以前永久代的OOM问题,元数据和class对象存在永久代中,容易出现性能问题和内存溢出,毕竟是和老年代共享堆空间。java8后,永久代升级为元空间独立后,也降低了老年代GC的复杂度。

参考文章:

jvm之年轻代(新生代)、老年代、永久代以及GC原理详解、GC优化 - 走看看

对方法区和永久代的理解 - 简书

如果出现了OOM怎么办?

解决方案:


1、修改JVM启动参数,直接增加内存。

JVM默认可以使用的内存为64M,Tomcat默认可以使用的内存为128MB,对于稍复杂一点的系统就会不够用。在某项目中,就因为启动参数使用的默认值,经常报“Out Of Memory”错误。因此,-Xms,-Xmx参数一定不要忘记加。

2、找出可能发生内存溢出的位置,并解决:

检查代码中是否有死循环或递归调用。
检查是否有大循环重复产生新对象实体。
检查对数据库查询中,是否有一次获得全部数据的查询。
检查List、Map等集合对象是否有使用完后,未清除的问题。
使用内存查看工具动态查看内存使用情况。
 

Dump文件

怎么去分析找到出现的内存溢出(OOM)问题?

 -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfHemoryError

OutOfHemoryError出现OOM错误,会在同级目录下生成个 文件

 

-Xms设置初始化内存分配  默认大小1/64

-Xmx设置最大分配内存,默认1/4

-XX: +PrintGCDetails     //打印GC垃圾回收信息

-XX : +HeapDumponoutofMemoryError     //oom DuMP

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JagTom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值