再也不用怕! 让你彻底搞明白Java内存分布

一、堆内内存

堆内内存分为三大部分,年轻代 , 老年代 和 元空间,所以 堆内内存 = 年轻代 + 老年代 + 元空间,下面细聊下三部分

1.1 年轻代-Young Generation

存放的是new 生成的对象

年轻代是为了尽可能快速的回收掉那些生命周期短的对象

Eden

  • 大部分对象在Eden区中生成
  • 当Eden区满时,会做一次young gc, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor区

Survivor(通常2个)

  • 当两个 Survivor 区 都满时, 从第一个Survivor 区 被复制过来 且 依旧存活的 对象,超过一定年龄的会被复制到 老年代(Tenured)
  • Survivor 的两个区是对称的, 没有先后关系, 所有同一个区中可能同时存在从Eden复制过来的对象 和 从前一个 Survivor 复制过来的对象。
  • 把age大于-XX:MaxTenuringThreshold的对象晋升到老年代;(对象每在Survivor区熬过一次,其age就增加一岁);

1.2 老年代 (Old Generation)

存放了在年轻代中经历了N次垃圾回收后仍存活的对象, 是一些生命周期较长的对象.

存放那些创建的时候占用空间比较大的对象,这些对象不经历eden,直接进入老年代,大对象(大小大于-XX:PretenureSizeThreshold的对象)

1.3 元数据(Meta space)

存放类的数据

存放静态文件, 如静态类和方法等。持久代对垃圾回收没有显著影响, 但是有些应用可能动态生成或者调用一些class, 比如Hibernate, Mybatis 等, 此时需要设置一个较大的持久代空间来存放这些运行过程中新增的类。

设置持久代大小参数: -XX:MetaspaceSize, -XX:MaxMetaspaceSize

1.4 总结

1、默认参数:

老年代占整个堆内存的2/3

年轻代占整个内存的1/3

Eden 区域占 整个年轻代的80%,From 和 To 两个生存者区域各占10%

2、新老年代相关jvm参数

-XX:NewRatio 设置新老年代比例,如-XX:NewRatio=5 代表 新老年代比例为1:5,新生代占用堆内存的1/6,老年代占用5/6;

-XX:SurvivorRatio 设置新生代中eden和两个2个Survivo区域大小的比例,如-XX:SurvivorRatio=8,则eden:s1:s2=8:1:1,默认比例就是为8:1:1.

3、young GC发生在新生代中,FUll GC 发生在整个堆空间中,一般是老年代空间不够用就会出发FULL GC

二、堆外内存

我们的游戏服务器使用的是netty,所以单说下netty,Netty的ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝,堆外内存的零拷贝.提升了效率。因为操作系统内核直接把数据写到堆外内存里,不需要像普通API一样,操作系统内核缓存一份,程序读的时候再复制一份到程序空间。

2.1 java中在堆外开辟内存的方法有两种

1.用DirectBufferByteBuffer.allocateDirect(size)

2.用JNI写java的c/c++扩展,在扩展里不牵扯jvm自己向系统搞内存出来。

2.2 使用堆外内存的优点

1.减少了垃圾回收因为垃圾回收会暂停其他的工作。

2.加快了复制的速度堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。

2.3堆外内存的缺点

内存难以控制,使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理,当发生内存溢出时排查起来非常困难。

三、垃圾回收

3.1 垃圾回收(GC)

Minor GC

  • 一般当新对象生成并且在Eden申请空间失败时就会触发MinorGC, 对Eden区域进行GC, 清除非存活对象, 并且把尚存活的对象移动到Survivor区, 然后整理两个Survivor区。
  • 该方式的GC是对年轻代的Eden区进行,不会影响到年老代。
  • 由于大部分对象是从Eden区开始的,  所以Eden区的GC会很频繁。

Major GC / Full GC

  • 老年代(Tenured) 被写满
  • 持久代(Permanent) 被写满
  • System.gc() 被显示调用
  • 上一次GC之后Heap 的各域分配策略动态变化
  • 对整个堆进行整理。
  • 所消耗的时间较长, 所以要尽量减少 Full GC 的次数
  • 出现Full GC经常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就可以选择设置Major GC策略);

Major GC速度一般比Minor GC慢10倍以上。

3.2 GC root

程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,是需要释放内存的对象。

java中可作为GC Root的对象有

1.虚拟机栈中引用的对象(本地变量表)

2.方法区中静态属性引用的对象

3方法区中常量引用的对象

4.本地方法栈中引用的对象(Native对象)

3.3常用垃圾回收器

垃圾收集器就是内存回收的具体实现。下面介绍一下虚拟机提供的几种垃圾收集器

Serial收集器(复制算法)

新生代单线程收集器,标记和清理都是单线程,优点是简单高效。

Serial Old收集器(标记-整理算法)

老年代单线程收集器,Serial收集器的老年代版本。

ParNew收集器(停止-复制算法) 

新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。

Parallel Scavenge收集器(停止-复制算法)

并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。

Parallel Old收集器(停止-复制算法)

Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先

CMS(Concurrent Mark Sweep)收集器(标记-清理算法)

高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择

G1(Garbage-First)

现在最新的回收器,新生代和老年代通用

新生代收集器使用的收集器:Serial、PraNew、Parallel Scavenge

老年代收集器使用的收集器:Serial Old、Parallel Old、CMS

我们线上服务器使用的是G1 收集器

四、总结

上面列举了很多的内容,但是需要记住的下面几点就可以了

1、对象的迁移路径:出生在Eden,然后在Survivor 区域来回迁移,迁移一次一次增加一次年龄,年龄太大的直接进入老年代

2、 Eden区域满了 产生 minor Gc

老年代满了产生 full gc

3、记住回收器是执行gc 的,选择最新的G1回收器就好了

到此这篇关于再也不用怕! 让你彻底搞明白Java内存分布的文章就介绍到这了,更多相关Java内存分布内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java虚拟机内存区域划分详解

    在谈 JVM 内存区域划分之前,我们先来看一下 Java 程序的具体执行过程,我画了一幅图. Java 源代码文件经过编译器编译后生成字节码文件,然后交给 JVM 的类加载器,加载完毕后,交给执行引擎执行.在整个执行的过程中,JVM 会用一块空间来存储程序执行期间需要用到的数据,这块空间一般被称为运行时数据区,也就是常说的 JVM 内存. 所以,当我们在谈 JVM 内存区域划分的时候,其实谈的就是这块空间--运行时数据区. 大家应该对官方出品的<Java 虚拟机规范>有所了解吧?了解这个规范可

  • 深入了解Java虚拟机栈以及内存模型

    1.结合字节码指令理解Java虚拟机栈和栈帧 栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间. 每个栈帧中包括局部变量表(Local Variables).操作数栈(Operand Stack).指向运行时常量池的引用(A reference to the run-time constant pool).方法返回地址(Return Address)和附加信息. 局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中,局部变量表中的变量不可直接使用,如需要使用的话,必须通过

  • Java基础之堆内存溢出的解决

    一.实战-内存溢出 堆内存溢出 栈内存溢出 方法区溢出 直接内存溢出 二.实战-堆内存溢出 演示堆内存溢出代码,并且定位问题 总结堆内存溢出的场景与解决方案 分析商城项目中可能存在堆内存溢出的代码并且解决 三.堆内存溢出演示代码 public class HeapOOMTest { private List<String> oomList = new ArrayList<>(); public static void main(String[] args) { HeapOOMTes

  • java各种类型对象占用内存情况分析

    前言 其实一般的程序猿根本不用了解这么深,只有当你到了一定层次,需要了解jvm内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化.等等等等,一句话,当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时,你会感谢这篇文章 本文主要分析jvm中的情况,实验环境为64位window10系统.JDK1.8,使用JProfiler进行结论验证 很多描述以及 概念是基于你懂基本java知识的,如果你看起来有点吃力,要加油咯 本片更偏重验证,更多理论,请参考:http

  • Java面向对象和内存分析图文详解

    一.Java类 类是面向对象编程中最基本的单位. Java中的类包含三个内容,分别是: 属性 属性又叫成员变量. 属性用于定义类或类对象的数据(静态特征). 范围为整个类体. 方法 方法用于定义类或类对象的行为特征(执行动作)(动态). 方法类似于面向过程中的函数,面向过程中的函数是最基本的单位: 而在面向对象中,最基本单位是类,方法从属于类和对象. 构造方法 构造方法分为无参构造方法:有参构造方法. 构造方法要与类名保持一致. 如果不设置构造方法,则系统自动生成无参构造方法. 属性的定义格式:

  • 详解Java对象创建的过程及内存布局

    一.对象的内存布局 对象头 对象头主要保存对象自身的运行时数据和用于指定该对象属于哪个类的类型指针. 实例数据 保存对象的有效数据,例如对象的字段信息,其中包括从父类继承下来的. 对齐填充 对齐填充不是必须存在的,没有特别的含义,只起到一个占位符的作用. 二.对象的创建过程 实例化一个类的对象的过程是一个典型的递归过程. 在准备实例化一个类的对象前,首先准备实例化该类的父类,如果该类的父类还有父类,那么准备实例化该类的父类的父类,依次递归直到递归到Object类. 此时,首先实例化Object类

  • 详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    一.堆栈内存 堆栈内存,顾名思义,指的是堆内存以及栈内存,其中,堆内存是由Java GC进行管理的内存区域,而栈内存则是线程内存.关于栈内存,这里不去细说.以Hotspot为例,堆内存的简要结构如下图所示: 而堆栈的关系,我们可以通过一行简单的代码来理解: public static void main(String[] args) { Object o = new Object(); } 上述代码主要完成了两件事,new Object( ) 在堆上开辟了一块内存,也就是说,new Object

  • Java内存分布归纳整理详解

    Java内存分布:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域:方法区.虚拟机栈.本地方法栈.堆.程序计数器. 1.程序计数器 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型中,字节码解释器工作时就是通过改变这个计数器的值来选取吓一条需要执行的字节码指令. 分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器

  • 再也不用怕! 让你彻底搞明白Java内存分布

    一.堆内内存 堆内内存分为三大部分,年轻代 , 老年代 和 元空间,所以 堆内内存 = 年轻代 + 老年代 + 元空间,下面细聊下三部分 1.1 年轻代-Young Generation 存放的是new 生成的对象 年轻代是为了尽可能快速的回收掉那些生命周期短的对象 Eden 大部分对象在Eden区中生成 当Eden区满时,会做一次young gc, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor区 Survivo

  • 一文搞明白Java Spring Boot分布式事务解决方案

    目录 前言 1. 什么是反向补偿 2. 基本概念梳理 3. 什么是两阶段提交 4. AT 模式 5. TCC 模式 6. XA 模式 7. Saga 模式 前言 分布式事务,咱们前边也聊过很多次了,网上其实也有不少文章在介绍分布式事务,不过里边都会涉及到不少专业名词,看的大家云里雾里,所以还是有一些小伙伴在微信上问我. 那么今天,我就再来一篇文章,和大家捋一捋这个话题.以下的内容主要围绕阿里的 seata 来和大家解释. 1. 什么是反向补偿 首先,来和大家解释一个名词,大家在看分布式事务相关资

  • 一篇文章带你搞定JAVA内存泄漏

    目录 1.什么是内存泄漏 2.内存泄漏的原因 3.内存泄漏有哪些情况 3.1 代码中没有及时释放,导致内存无法回收. 3.2 资源未关闭造成的内存泄漏 3.3 全局缓存持有的对象不使用的时候没有及时移除,导致一直在内存中无法移除 3.4 静态集合类 3.5 堆外内存无法回收 4.内存泄漏的解决办法 5.内存问题排查 第一步 首先确认逻辑问题 第二步:分析gc是否正常执行 第三步 确认下版本新增代码的改动,尽快从代码上找出问题. 第四步:开启各种命令行和 导出 dump 各种工具分析 总结: 1.

  • 学会用Python实现滑雪小游戏,再也不用去北海道啦

    一.效果图 二.必要工具 Python3.7 pycharm2019 再然后配置它的文件,设置游戏屏幕的大小,图片路径. 代码如下 '''配置文件''' import os '''FPS''' FPS = 40 '''游戏屏幕大小''' SCREENSIZE = (640, 640) '''图片路径''' SKIER_IMAGE_PATHS = [ os.path.join(os.getcwd(), 'resources/images/skier_forward.png'), os.path.j

  • 这么设置IDEA中的Maven,再也不用担心依赖下载失败了

    一.Maven 设置 当我们下载安装 Maven 之后,如果不修改 maven 中 setting 文件的. 那默认情况下, Maven 远程中央仓库地址为是个国外的地址. http://repo1.maven.org/maven2 那我们国内的网络,因为神秘的力量的影响,访问国外的地址就会比较慢. 如果你的网络情况很差,那么用默认的地址下载依赖资源就会很慢,有可能运行到一半下载失败. 那这种情况下,我们可以通过设置 Maven 仓库镜像地址从而解决这个问题. 那国内可以用的 Maven 的镜像

  • 再也不用花钱买漫画!Python爬取某漫画的脚本及源码

    一.工具 python3 第三方类库requests python3-pyqt5(GUI依赖,不用GUI可不装) ubuntu系列系统使用以下命令安装依赖: URL格式: 漫画首页的URL,如http://m.ac.qq.com/Comic/view/id/518333(移动版) 或 http://ac.qq.com/Comic/comicInfo/id/17114, http://ac.qq.com/naruto(PC版) 注意: 火影忍者彩漫需要访问m.ac.qq.com搜索火影忍者,因为P

  • 三大Python翻译神器再也不用担心学不好英语

    导语 ​ hello everyone!  I'm kimiko! ​Miss me??? 嘿!中秋结束了,开始正式营业给大家继续送福利啦! ​ 中秋在家发现:家里的小孩子在最后上学那天开始赶作业了!果然每个孩子的童年都是一样的!哈哈哈啊~ 看了一眼侄女儿正在写英语翻译题目, 啊这一看不得了题目写的乱七八糟!!! 这让我有点儿上头啊,刚巧直接给我姐说了:'''这英语还要加强学习撒' 老姐说:''回家我也不会教不了,学校的也没学好,一句完整的句子都讲不出来.......'" ​ 那肯定是拿上我30

  • Python永久配置国内镜像源安装再也不用担心卡顿

    目录 问题来源 配置国内镜像源 问题来源 今天在使用pip install xlutils安装xlutils包的时候,一直出现了该错误 ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out. 可能原因是由于,使用这种方式安装某些库的时候,会从国外的镜像源去下载,最终导致反应超时的情况发生. 太扎心了,有时候想要使用某个库,却一直下载不下来,着急没办法呀!如何解决

  • 一文搞懂Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好. 好了,言归正传. 反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车).先举一个小栗子

  • 两个小例子轻松搞懂 java 中递归与尾递归的优化操作

    废话不多说,我们直接上两个最常见的小例子: 一.递归,伪递归,迭代实现n! package com.njbdqn.test02; /** * 递归,伪递归,迭代实现n! */ public class RecursionTest { public static void main(String[] args) { System.out.println(recurse(5)); //递归显示 System.out.println(camouflageRecurse(5, 1)); //伪递归 Sy

随机推荐