**前言**

由于先前也遇到过一些性能问题,OOM算是其中的一大类了。因此也对jvm产生了一些兴趣。自己对jvm略做了些研究。后续继续补充。

**从oom引申出去**

既然说到oom,首先需要知道oom的原因是什么。为啥会oom嘞?
oom的定义是outofmemory。当内存想为对象分配内存的时候,发现内存不足以去分配内存,或者gc的时候发现没有可以被回收的对象或回收后的内存也不足以为对象分配内存。
因此抛出这个java异常。

oom
可以分为以下四类

1.堆溢出:java堆

2.栈溢出:虚拟机栈和本地方法栈

3.方法区内存溢出:方法区和内存时常量池

4.本机直接内存溢出

因此,需要先了解堆,栈,方法区都是些啥

**运行时数据区**

先上图
![file](https://img2018.cnblogs.com/blog/1867456/201911/1867456-20191121171630048-985603557.jpg)

**程序计数器**:当前线程所执行的字节码的行号指示器。

java虚拟机的多线程是通过轮流切换线程,并为线程分配执行时间片去运行来执行的。每个线程都有一个自己的程序计数器。我觉得这个可以这么理解:当一个线程在运行的时候,每执行一步程序计数器都会有个记录,记录当前执行到哪一步了。如果线程被切换后又切换回来,那么通过程序计数器就能知道执行到哪一步了,然后继续向下执行。

**虚拟机栈**:每个线程都会有一个虚拟机栈。虚拟机栈描述的是java方法执行的内存模型。因为线程执行的过程就是执行线程里的一个个方法,而每个方法都会创建对应自己的栈帧。

栈帧里存的内容如下:

- **局部变量表**:存放了各种编译期可知基本数据类型,对象引用(引用指针或句柄)

- **操作数栈**:大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈

- **动态链接**

- **方法出口**

64位的long和都double类型数据占用2个局部变量空间,其他数据类型占用一个,也就是每个局部变量空间为32位。

在这个地方,如果线程请求的深度大于虚拟机允许的深度,会抛出**StackOverflowError**.因为jvm分配给虚拟机栈的内存是有限的,而每个方法都会有对应的栈帧压入到栈中,如果调用方法过多,那么栈满了自然也就溢出了。(可能的场景:死循环代码,大量递归调用,那排查问题的时候也可以由此有一个思路)。可以通过调整**-Xss**去调整栈大小。

大部分java虚拟机允许动态扩展,但如果扩展的时候也申请不到足够内存时,就会报OOM了。

**本地方法栈**:和虚拟机发挥作用相似。区别:虚拟机栈为虚拟机执行java方法服务,本地方法栈为虚拟机使用的Native方法服务。Native Method就是一个java调用非java代码的接口,Native方法的实现由非java语言实现。读者不用纠结,略作了解即可。

**堆**:堆是所有线程共享的一块内存,作用是存放对象实例。堆可以分为新生代和老年代。新生代里还可细分为Eden,From survivor,To survivor等空间。后面讲述GC过程时会说到。

**方法区**:也是所有线程共享的一块内存,存放被虚拟机加载的类信息,常量,静态变量,编译器编译后的代码。也就是常说的永久代。

永久代的大小可以用**-XX:MaxPermSize**去设置。

**运行时常量池**:方法区的一部分。存放编译期生成的各种字面量和符号引用。字面量就是指这个量本身。比如字面量2,就是指2.

运行时常量池有一个重要特性就是动态性。常量不一定只有编译期才能产生,运行期间也可能将新的常量放入常量池。详情可见String类的

intern()方法。

此处推荐这篇博客,对intern()方法介绍的挺清楚的。

https://blog.csdn.net/soonfly/article/details/70147205

**直接内存**:它不是虚拟机运行时数据区的一部分,但也频繁的被使用。直接内存不会受到java堆大小的限制,但是会受到本机总内存的限制。

**GC过程**

GC分为新生代GC(minor gc)和老年代GC(full gc)。新生代GC的频率远远高于老年代。而且

**新生代GC的速度会比老年代的GC速度快10倍以上。根源在于新生代和老年代使用的GC算法不同。读者们可以去仔细思考下(答案文中有,哈哈)**。新生代/老年代大小默认为1:2。

**新生代GC过程**:

新生代里可细分为Eden,From survivor,To survivor等空间。当我们需要给对象分配内存的时候,首先我们会在Eden区为对象分配内存,当Eden区内存不足时,会发生minor gc,此时会把仍然存活的对象放到From survivor,并给对象标记存活次数1;然后当Eden区再次被用完后,对Eden区和From survivor区筛选出存活的对象,放到To survivor区,清空Eden区和From survivor区,存活次数加1,之前存活的就是2了。

以此类推,默认是当存活次数到达15次(可配置)的时候,把这个对象存入老年代中。同时也可以看到,From survivor,To survivor区始终有一个是空置的。所以新生代使用的只有9/10的空间。
然而大家可以思考一下。Eden区和survivor区的大小为8:1,那么发生minor gc后如果存活的对象
的大小比survivor区还要大。这个时候会怎么处理?

这里需要引入一个叫“内存分配担保机制”的概念。就是当存活的对象连survivor区都放不下的时候,这部分放不下的对象会直接进入老年代。老年代是担保人。老年代进行担保,前提是老年代还有剩余空间。但是每次存活下来的对象大小是不确定的。所以只好取之前每次存储到老年代的对象大小的平均值。如果大于平均值,那么直接full gc。但是为了避免频繁full gc,仍然会开启handlepromotionfailure配置。如下图

![file](https://img2018.cnblogs.com/blog/1867456/201911/1867456-20191121171630327-891484811.jpg)

**老年代GC过程**:

老年代采用了标记整理,标记清楚的算法。老年代会把仍然存活的对象都整理统一放到一边。整理完成后就会清楚掉边界外的对象。这样就避免了产生大量的内存碎片的问题。但是整理算法相较于新生代采用的复制算法,复杂程度肯定更高。这也导致了full gc的速度要远远慢于minor gc。
文中若有考虑不周或者错误,欢迎大家支招指正。一起学习交流。betterFighter!

JVM浅谈的更多相关文章

  1. 结合JVM 浅谈Java 类加载器(Day_03)

    所谓错过,不是错了,而是过了. 什么是JAVA类加载? Class对象由JVM自动产生,每当一个类被加载时,JVM就自动为其生成一个Class对象,通过Class对象可以获得类的相关信息.将类信息读取 ...

  2. 安装JDK后JRE与JVM联系浅谈

    转自安装JDK后JRE与JVM联系浅谈 安装JDK后JRE.JVM之间的关系是什么呢?那么我们要从安装JDK慢慢说起. 如果安装了JDK,会发同你的电脑有两套JRE: 一套位于 <JDK安装目录 ...

  3. 浅谈jvm中的垃圾回收策略

    下面小编就为大家带来一篇浅谈jvm中的垃圾回收策略.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧   java和C#中的内存的分配和释放都是由虚拟机自动管理的,此前我已 ...

  4. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  5. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  6. 浅谈Android应用性能之内存

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 文/ jaunty [博主导读]在Android开发中,不免会遇到许多OOM现象,一方面可能是由于开 ...

  7. java反射机制浅谈

    一.Java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  8. 【转】浅谈Java中的hashcode方法(这个demo可以多看看)

    浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...

  9. 浅谈web应用的负载均衡、集群、高可用(HA)解决方案(转)

    1.熟悉几个组件 1.1.apache     —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代理服务器,支持安 ...

随机推荐

  1. 真——Springcloud支持Https

    很久不写了,因为一直没有一个项目的需求推动,担心写的东西可能不是太实际.其间学习的事倒是做了不少,设计模式.领域开发.Antlr.kubernetes等等,其实大部分都记在纸质笔记上了.. 基于对新技 ...

  2. Go语言系列开发之延迟调用和作用域

    Hello,各位小伙伴大家好,我是小栈君,最近一段时间我们将继续分享关于go语言基础系列,当然后期小栈君已经在筹划关于java.Python,数据分析.人工智能和大数据等相关系列文章.希望能和大家一起 ...

  3. css3实现饼状图进度及环形进度条

    1 <!-- 饼图 --> <div class="pie"></div> <hr /> <!-- 环形图 --> &l ...

  4. MySQL注入--Payload

    MySQL注入--Payload Mirror王宇阳 2019-10-22 SQL的注入流程一般如下: 1.判断是否有SQL注入漏洞(判断注入点) 2.判断数据库的系统架构.数据库名.web应用类型等 ...

  5. Python 命令行之旅:使用 docopt 实现 git 命令

    作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...

  6. VirtualBox NAT Network配置

    VirtualBox NAT Network配置(OSX上的) VirtualBox的5种连接方式 NAT :虚拟机之间不能互通 NAT网络 :本文对象 桥接 :一般情况下虚拟机无法设置静态IP,并且 ...

  7. stream流篇

    流是C#中比较重要的一个概念,很多关键技术都需要用到流.何为流呢?可以理解流为江河中水的流动,不过C#中则为信息流,我们可以把信息写入流,也可以读出.比如以文件读写操作为例,首先以某种方式(如只读)打 ...

  8. GCC常用参数详解

    转载:http://www.cnblogs.com/zhangsir6/articles/2956798.html 简介gcc and g++现在是gnu中最主要和最流行的c & c++编译器 ...

  9. 二叉树,红黑树,B树,B+树

    1.不要认为红黑树仅仅是在二叉树的节点上涂上颜色,他们最根本的区别是,红黑树根据节点涂色的约束限制,最终形成的树的结构与普通二叉树不同,最重要的是,其树的高度大大缩短,从而在查找.增删改等方面提高效率 ...

  10. JavaScript基础修炼(14)——WebRTC在浏览器中如何获得指定格式的PCM数据

    目录 一. PCM格式是什么 二. 浏览器中的音频采集处理 三. 需求实现 方案1--服务端FFmpeg实现编码 方案2--ScriptProcessorNode手动处理数据流 参考文献 示例代码托管 ...