jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

jvm gc(垃圾回收机制)

Java JVM  垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

  • 前言:(先大概了解一下整个过程)
  • 作者:知乎用户
    链接:https://www.zhihu.com/question/27339390/answer/36511809
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
     

    java堆(JavaHeap)
    1.用来存放对象的,几乎所有对象都放在这里,被线程共享的,或者说是被栈共享的
    2.堆又可以分为新生代和老年代,实际还有一个区域叫永久代,但是jdk1.7已经去永久代了,所以可以当作没有,永久代是当jvm启动时就存放的JDK自身的类和接口数据,关闭则释放。
    新生代可以分为Eden区和两个幸存区,这么设计是为了更好地利用内存 之前的设计是只分为两部分一样一半 后来发现这样只利用到了一半的内存 才改为按比例分成三个区的,使用的是复制回收算法,两个幸存区是较小的区域。逻辑是每次使用Eden区和其中一个幸存区,回收时将其还存活着的对象一次性的复制到另一个幸存区中,最后清理到刚才使用的Eden和其中一个幸存区。
    美团的面试官也问了这个问题,他也说了他的理解,我感觉可能是不准确的。
    新建对象就在Eden区,Eden就是伊甸,顾名思义。但是并不是对象最活跃的区域,对象最活跃的区域是老年代,因为经过各种垃圾回收之后对象都跑到这里来了。
    3.内存溢出
    内存溢出其实没什么好讲的,满了就会溢出。怎么才能满呢,不断创建对象,那问题又来了,创建多了被回收怎么办,好办,将新建的对象存到list里去,就不会回收了,为什么呢,因为jvm判定一个对象的死活就是根据对象是不是被引用。
    此外堆跟随jvm的,有jvm就有堆。堆也是垃圾回收的主要区域,又叫GC堆,垃圾堆,玩笑。
    jvm栈
    1.要说栈是用来存什么的,其实我感觉不严谨,栈是运行时创建的,是跟随线程的,它不是用来存什么的,那它用来干什么的,它是用来存栈帧的,没有图不太好说呢,等下我去截个图。

    <img src="https://pic1.zhimg.com/50/f7541e5d33d1d8b412dd0556c7e4b10d_hd.jpg" data-rawwidth="481" data-rawheight="713" class="origin_image zh-lightbox-thumb" width="481" data-original="https://pic1.zhimg.com/f7541e5d33d1d8b412dd0556c7e4b10d_r.jpg">图来了我就不用多说了。每个栈帧其实可以理解为一个方法,我是这么理解的,之间的关系就是调用。
    2.栈的好处就是不需要垃圾回收,随着线程结束内存就释放。
    3.但是并不是说就不会内存溢出,那么栈的内存溢出是怎么产生的呢,肯定也是满了,这个满了怎么理解呢,一是要申请的不够了,二是jvm内存太小,这是个有趣的问题。但是产生的错误却是不一样的,如果创建一个void方法调用自身,错误是stackoverflowError,如果不断创建线程则会outOfMemoryError。这里就有一个比较高级的问题了,对于第二种多线程内存溢出该怎么解决呢,深入理解jvm一书中给出的解决方案是这样的,通过减小最大堆和栈容量来换取更多的线程。
    方法区和运行时常量池
    1.方法区是堆的一个逻辑区域,但是又叫非堆。运行时常量池又是方法区的一部分,真正的一部分。方法区并不是存方法的,存方法的应该是栈或者栈帧。方法区存的是类信息、常量、静态变量等,也是被线程共享的区域。运行时常量池存放的是编译期生产的各种字面量和符号引用。
    2.这块内存区域的回收没啥好说的,因为我也不太清楚,我只知道HotSpot的设计团队选择把GC分代扩展至方法区了,或者是使用永久代实现方法区。
    3.内存是肯定会溢出的,不断创建类会导致方法区内存溢出,而不断将常量放入常量池(String.intern()),常量池也会内存溢出。
  •  

  • 这里主要讲分代回收机制
  1. 内存结构
  • 年轻代:一个 Eden 区和两个 Survivor 区
  • 年老代:一个 old 区
  • 持久代:一个 Permanent 区
  1. 新建立的对象先放到 Eden 区中,如果 Eden 区满了之后,就会执行标记-清除算法回收 Eden 区垃圾,并把生存的对象放到 Survivor 的其中一个区中,两个 Survivor 区有一个必须是空的,当其中一个 Survivor 满了之后,采用标记-复制方法,把生存的对象放到另外一个 Survivor 区。

  2. 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。

  3. 持久代用于存放静态文件、Java类、方法、静态对象等。

  • 触发 gc 的条件
  1. minor GC: 当新对象生成,并且在Eden申请空间失败时,就会触发minor GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区

  2. Full GC: 对整个堆进行整理,包括Young、old 和Perm。Full GC因为需要对整个对进行回收,以下原因可能导致 Full GC

  • 年老代(old )被写满
  • 持久代(Perm)被写满
  • System.gc()被显示调用
  • 上一次GC之后Heap的各域分配策略动态变化

 

jvm参数说明~~

 

 

1.jvm的结构:

 

  • 方法区: 也是 jvm gc 中的持久代,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。

  • 堆: 也是被各个线程共享的内存区域,在JVM启动时创建。该内存区域存放了对象实例及数组,包括 jvm gc 中的年轻代和年老代。

  • 虚拟机栈: 每个方法被执行的时候 都会创建一个“栈帧”用于存储局部变量表(包括参数)、操作栈、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。声明周期与线程相同,是线程私有的。而局部变量表中继承 Object 的对象都是引用堆和方法区的内存,而基本数据类型的对象(boolean、byte、char、short、int、float、long、double)则是直接保存存在栈中。

  • 本地方法栈: 与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。

  • 程序计数器: 类似于PC寄存器,是一块较小的内存区域,通过程序计数器中的值寻找要执行的指令的字节码,由于多线程间切换时要恢复每一个线程的当前执行位置,所以每个线程都有自己的程序计算器。

 

  1. 各参数说明
  • -Xmx: 堆内存大小的上限

  • -Xms: 堆内存大小的初始值

  • -Xmn: 年轻代内存大小,年轻代包括两个区,Eden 和 Survivor 区,Suvrvior 区还被平均分成了两块 from space 和 to space

  • -Xss: 每条线程内存大小

  • -XX:PermSize(java8 之后变成了 -XX:MetaspaceSize): 持久代初始内存大小

  • -XX:MaxPermSize(java8 之后变成了 -XX:MaxMetaspaceSize): 最大持久代内存大小

  • -XX:NewSize: 新生代初始堆内存占用的默认值

  • -XX:MaxNewSize: 新生代占整个堆内存的最大值

  • -XX:NewRatio: 老年代对比新生代的空间大小, 比如2代表老年代空间是新生代的两倍大小

  • -XX:SurvivorRatio: Eden/Survivor的值,比如8表示Survivor:Eden=1:8, 因为survivor区有2个, 所以Eden的占比为8/10

  • -XX:CompressedClassSpaceSize: 类指针压缩空间大小

  1. 指针压缩介绍
  • 64位平台上默认打开
  • 使用-XX:+UseCompressedOops压缩对象指针 
    "oops"指的是普通对象指针("ordinary" object pointers) 
    Java堆中对象指针会被压缩成32位 
    使用堆基地址(如果堆在低26G内存中的话,基地址为0)
  • 使用-XX:+UseCompressedClassPointers选项来压缩类指针 
    对象中指向类元数据的指针会被压缩成32位 
    类指针压缩空间会有一个基地址

 

 

  1. 元空间和类指针压缩空间的区别
  • 类指针压缩空间只包含类的元数据,比如InstanceKlass, ArrayKlass 
    仅当打开了UseCompressedClassPointers选项才生效 
    为了提高性能,Java中的虚方法表也存放到这里

  • 元空间包含类的其它比较大的元数据,比如方法,字节码,常量池等

 

 

 

Java JVM  垃圾回收(GC 在什么时候对什么东西,做了什么事情

 

 

在什么时候

首先需要知道,GC又分为minor GC 和 Full Gc(也称为Major GC)。Java 堆内存分为新生代和老年代(持久代在方法区上),新生代中又分为1个Eden区域 和两个 Survivor区域

那么对于 Minor GC 的触发条件:大多数情况下,直接在 Eden 区中进行分配。如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC

 

对于 Full GC(Major GC)的触发条件:也是如果老年代没有足够空间的话,那么就会进行一次 Full GC。

Ps:上面所说的只是一般情况下,实际上,需要考虑一个空间分配担保的问题:

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于则进行Minor  GC,如果小于则看HandlePromotionFailure设置是否允许担保失败(不允许则直接Full GC)。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试Minor GC(如果尝试失败也会触发Full GC),如果小于则进行Full GC。

空间分配担保总结:

minor GC :1. 老年代最大连续可用的空间大于新生代所有对象的总空间; 2. 老年代最大连续可用空间小于新生代所有对象的总空间,并且HandlePromotionFailure设置允许担保失败,且老年代最大可用连续空间大于历次晋升老年代对象的平均大小。

full GC :1. 老年代最大连续可用的空间小于新生代所有对象的总空间,并且HandlePromotionFailure设置不允许担保失败;

2。 如果上面的2尝试minor GC失败,则出发full GC。

 

 

但是,具体到什么时刻执行,这个是由系统来进行决定,是无法预测的。

对什么东西(总结:从GC root开始搜索,搜索不到的对象,并且经过第一个标记,清理之后,仍然没有复活的对象)

 

主要根据可达性分析算法,如果一个对象不可达,那么就是可以回收的;如果一个对象可达,那么这个对象就不可以回收。对于可达性分析算法,它是通过一系列称为“GC Roots” 的对象作为起始点,当一个对象到 GC Roots 没有任何引用链相接的时候,那么这个对象就是不可达,就可以被回收。如下图:

 

这个GC Root 对象可以是一些静态的对象,Java方法的local变量或参数, native 方法引用的对象,活着的线程。

做了什么事情

主要做了清理对象,整理内存的工作。Java堆分为新生代和老年代,采用了不同的回收方式。例如新生代采用了标记复制算法,老年代采用了标记整理法。在新生代中,分为一个Eden 区域和两个Survivor区域,真正使用的是一个Eden区域和一个Survivor区域,GC的时候,会把存活的对象放入到另外一个Survivor区域中,然后再把这个Eden区域和Survivor区域清除。那么对于老年代,采用的是标记整理法,首先标记出存活的对象,然后再移动到一端。这样也有利于减少内存碎片。

 

 

 

 

额外补充:

 

 

  1. SafePoint是什么

比如GC的时候必须要等到Java线程都进入到safepoint的时候VMThread才能开始执行GC,

  1. 循环的末尾 (防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint)
  2. 方法返回前
  3. 调用方法的call之后
  4. 抛出异常的位置
  1. GC收集器有哪些?CMS收集器与G1收集器的特点。

行收集器:串行收集器使用一个单独的线程进行收集,GC时服务有停顿时间

并行收集器:次要回收中使用多线程来执行

CMS收集器是基于“标记—清除”算法实现的,经过多次标记才会被清除

G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的

[GC收集器]: http://www.jianshu.com/p/50d5c88b272d

  1. 类加载的几个过程:

加载、验证、准备、解析、初始化。然后是使用和卸载了

通过全限定名来加载生成class对象到内存中,然后进行验证这个class文件,包括文件格式校验、元数据验证,字节码校验等。准备是对这个对象分配内存。解析是将符号引用转化为直接引用(指针引用),初始化就是开始执行构造器的代码

  1. 双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

Bootstrap ClassLoader:启动类加载器,负责将$ Java_Home/lib下面的类库加载到内存中(比如rt.jar

Extension ClassLoader:标准扩展(Extension)类加载器,它负责将$Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。

ApplicationClassLoader:它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器

双亲委派模型是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。-----例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱

  1. 分派

静态分派(重载)与动态分派(重写)。

  1. 你知道哪些JVM性能调优

设定堆最小内存大小-Xms

  1. -Xmx:堆内存最大限制。
  2. 设定新生代大小。
    新生代不宜太小,否则会有大量对象涌入老年代

-XX:NewSize:新生代大小

-XX:NewRatio 新生代和老生代占比

-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比

  1. 设定垃圾回收器

​ 年轻代用 -XX:+UseParNewGC (串行) 年老代用-XX:+UseConcMarkSweepGC (CMS)

  1. 设定锁的使用

多线程下关闭偏向锁,比较浪费资源

  1. g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择

1CMS是一种以最短停顿时间为目标的收集器

响应优先选择CMS,吞吐量高选择G1

  1. 当出现了内存溢出,你怎么排错

用jmap看内存情况,然后用 jstack主要用来查看某个Java进程内的线程堆栈信息

 

参考: http://icyfenix.iteye.com/blog/715301

 

https://github.com/konginyan/Learning-Notes/blob/master/java/jvm%20%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E.md

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Mathematical Model】Ransac线性回归Python代码

Ransac算法,也称为随机抽样一致性算法,是一种迭代方法,用于从一组包含噪声或异常值的数据中估计数学模型。Ransac算法特别适用于线性回归问题,因为它能够处理包含异常值的数据集,并能够估计出最佳的线性模型。 1 简介 …

异常解析————Parameter metadata not available for the given statement

引言 在将数据存入mysql数据库时抛出异常:Parameter metadata not available for the given statement。参数元数据对于给定的声明不可用。 SQL本身并没有错误: Autowiredprivate JdbcTemplate jdbc;public Integer saveScenicSequence(ScenicSequence…

Swagger使用————接口参数注解的使用缺陷

问题描述 在使用springboot开发web项目时,用到了swagger框架,来生成web api文档。但是其中有一项是举例说明参数的结构,如下图:但是,这个功能真的是非常方便,因为可以让前端开发人员第一时间得知参数的内部…

分布式事务最终一致性常用方案

分布式事务最终一致性常用方案目前的应用系统,不管是企业级应用还是互联网应用,最终数据的一致性是每个应用系统都要面临的问题,随着分布式的逐渐普及,数据一致性更加艰难,但是也很难有银弹的解决方案,也并…

数据列表的分页实现————分页敏捷开发

概要 分页功能是比较常见的基础功能,虽然比较简单,但是每次需要用到这个功能的时候还是需要现写一遍。为了实现更加宏观的业务复用,特将本人特别喜欢的简易分页逻辑在此记述,以备日后重用。 逻辑描述 一般的分页实现方式多是通…

Eclipse深度患者设置VSCode快捷键

VSCode设置Eclipse中常用的快捷键 将eclipse中一些基本的快捷键输入右侧用户快捷键设置中: // Place your key bindings in this file to overwrite the defaults [{ "key": "alt/", "command": "editor.action.triggerSugges…

NodeJS学习————关于let和const命令的使用理解

let的基本用法 在新的js规范ES6中,新增了let 命令,用来声明变量。用法类似于var,但不同的是所声明的变量,只在let 命令所在的代码块内有效。 { let a 10; var b 10; } //ReferenceError: a is not defined console.log(a …

forward和redirect的区别是什么?

forward和redirect是什么? 是servlet种的两种主要的跳转方式。forward又叫转发,redirect叫做重定向。 区别:(本地效应次数) 地址栏,数据共享,应用场景,效率,本质&…

MYSQL的索引类型:PRIMARY, INDEX,UNIQUE,FULLTEXT,SPAIAL 有什么区别?各适用于什么场合?

一、MySQL索引类型 MySql常见索引类型有:主键索引、唯一索引、普通索引、全文索引、组合索引 PRIMARY KEY(主键索引) ALTER TABLE table_name ADD PRIMARY KEY ( column ) UNIQUE(唯一索引) ALTER TABLE table_name ADD UNIQUE (colu…

Servlet入门总结

一、了解Servlet的概念Servlet定义:Servlet是基于Java技术的Web组件,由容器管理并产生动态的内容。Servlet引擎作为WEB服务器的扩展提供支持Servlet的功能。Servlet与客户端通过Servlet容器实现的请求/响应模型进行交互。 注意:Servlet不是从…

MySQL日期类型的处理总结

一、概述 MySQL中的日期类型包括以下5种: 类型大小 (字节)范围格式用途DATE31000-01-01/9999-12-31YYYY-MM-DD日期值TIME3-838:59:59/838:59:59HH:MM:SS时间值或持续时间YEAR11901/2155YYYY年份值DATETIME81000-01-01 00:00:00/9999-12-31 23:59:59YYYY-MM-DD HH:…

详解HTTP协议~~~

详解HTTP协议~~~HTTP 简介HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。。HTTP是一个基于TCP/IP通信协议来传递数据(…

Mybatis Plus————代码生成器

代码生成器 MyBatis Plus是MyBatis的扩展框架,而代码生成器是MP的核心功能之一,另外还有 “条件构造器”和“通用CRUD”等功能。 步骤演示 mp的代码生成器有两种方式自动生成代码,一种是通过main方法来执行程序,另一种是通过maven…

Spring MVC 流程图解析

Spring MVC 流程图解析Spring MVC工作流程图图一图二 SpringMVC工作流程描述DispatcherServlet,HandlerMapping,HandlerExecutionChain,HandlerAdapter,HttpMessageConveter,BindingResult,ModelAndView&am…

Java并发编程实战————可重入内置锁

引言 在《Java Concurrency in Practice》的加锁机制一节中作者提到: Java提供一种内置的锁机制来支持原子性:同步代码块。“重入”意味着获取锁的操作的粒度是“线程”,而不是调用。当某个线程请求一个由其他线程持有的锁时,发出…

java的守护进程与非守护进程

java的守护进程与非守护进程 最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程: User Thread(用户线程)、Daemon Thread(守护线程) ,(PS:以前忽略了&a…

双剑合璧————Spring Boot + Mybatis Plus

引言 最近在学习Mybatis Plus的使用,希望通过spring boot快速将mybatis plus整合进来。 对于springboot项目,mybatis plus团队也有自己的启动器 :mybatis-plus-boot-starter。这个依赖内部已经整合了mybatis-spring,也包括非快速…

Git初学札记(一)————Git简介与安装

前言 Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。(在这里再一次致敬Linus大神)特点 分布式相比于集中式的最…

Git初学札记(二)————EGit导入远程Git仓库项目(Clone操作)

引言 我们在实际开发项目的时候,难免要使用像Eclipse或者IDEA这样的继承开发工具,除了部分“牙牙学语”的程序员需要手动输入javac去编译程序以外,在实际开发中手动编译并运行项目的“猿族”应该是已经绝种了。 我个人认为,使用gi…

Git初学札记(三)————创建Git版本库

引言 版本库即所谓的Git仓库,英文名称是Repository,可以简单理解为一个目录(.git folder),这个目录可以记录并保存直接父级及其子目录下的全部文本文件的修改操作,谓之“版本控制”! 手动建库 不…