java内存模型和垃圾回收
摘抄并用于自查
JVM内存模型
1. Java程序具体执行的过程:
- Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀)
- 由JVM中的类加载器加载各个类的字节码文件,加载完毕后,交由JVM执行引擎执行
- 在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为 Runtime Data Area(运行时数据区),也就是我们常说的JVM内存
- 因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)
2. JVM的内存划分和各区域职责:
- 程序计数器:程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直到执行完所有的指令。(注:JVM中的程序计数器并不像汇编语言中的程序计数器一样是物理概念上的CPU寄存器,但是逻辑作用上是等同的,在JVM中多线程是通过线程轮流切换来获得CPU执行时间的,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序,因此,可以说,程序计数器是每个线程所私有的)
- Java栈:Java栈是Java方法执行的内存模型,Java栈中存放的是一个个的栈帧,每个栈帧(包括:局部变量表、操作数栈、运行时常量池(方法区内)的引用、方法返回地址和一些额外的附加信息)对应一个被调用的方法,当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕后,便会将栈帧出栈(注:由于每个线程正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,互不干扰)
- 本地方法栈:Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的
- 堆:Java中的堆是用来存储对象本身以及数组
- 方法区:它与堆一样,是被线程共享的区域,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等(注:在方法区中有一个非常重要的部分就是运行时常量池,他是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中)
垃圾回收机制
Java垃圾回收机制主要负责两件事
1. 发现无用对象
2. 回收被无用对象占用的内存空间,使之再次被程序使用(一般在CPU空闲或者内存不足时)
注,事实上,除了释放没用对象占用的内存空间外,垃圾回收也可以清除内存记录碎片(由于创建对象和垃圾回收器释放丢弃对象所占的内存空间)
特点
1. 垃圾回收机制的工作目标是回收无用对象的内存空间,这些内存空间都是 jvm堆内存(运行时数据区,用以保存类的实例,即对象)里的内存空间,不包含其他物理资源,比如数据库连接,磁盘I/O等
2. Java语言没有显式的提供分配内存和删除内存的方法,一些开发人员将引用对象设置为 null 或者调用 System.gc()或者 Runtime.getRuntime.gc()来释放内存(后两种方法,不能用)
3. 垃圾回收不可预知,不同的 jvm 采用不同的垃圾回收机制和算法,有可能定时发生,有可能CPU空闲时发生,也有可能内存耗尽时发生(最常用的分代回收)
分代回收
1. 年轻代(Young Generation)
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速收集那些生命周期短的对象。年轻代分3个区,一个 Eden区,两个 Survivor 区(一般而言,可以设置多个)。大部分对象在 Eden区中生成,当Eden区满时,还存活的对象被复制到 Survivor 区,当这个 Survivor区也满了的时候,从第一个 Survivor区复制过来的并且还存活的对象,将被复制在年老区(Tenured)。需要注意的是,Survivor的两个区是对称的,没有先后关系,所以同一个区中,可能同时存在从 Eden复制过来的对象,和从前一个 Survivor复制过来的对象,而复制到年老区的只有从第一个 Survivor区过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的,这样可以增加对象在年轻代中存在的时间,减少被放到年老代的可能。
2. 年老代(Old Generation)
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中,因此,可以认为年老代中存放的都是一些生命周期较长的对象。
3. 持久代(Permanent Generation)
用于存放静态文件,如,java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如 Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过 -XX:MaxPermSize=<N>进行设置。
4. 什么情况下触发垃圾回收
由于对象进行了分代处理,因此垃圾回收区域时间也不一样。GC有两种类型,Minor GC 和 Full GC。
Minor GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发 Minor GC,对 Eden 区域就行GC,清除非存活对象,并且把尚且存活的对象移动到 Survivor区。然后整理Survivor的两个区。这种方式的 GC是对年轻代的 Eden区进行,不会影响到年老代。因为大部分对象都是从 Eden区开始的,同时,Eden区不会分配的很大,所以 Eden区的 GC会频繁进行。因而,一般这里需要使用速度快、效率高的算法,使 Eden区能尽快空闲出来。
Full GC
对这个堆进行整理,包括 Young、Tenured 和 Perm。Full GC 因为需要对整个进行回收,所以比 Minor GC 要慢,因此应该尽可能减少 Full GC的次数。在对 JVM 调优的过程中,很大一部分工作就是对 Full GC 的调节。有如下原因可能导致 Full GC:
年老代(Tenured)被写满
持久代(Perm)被写满
System.gc() 被显式调用
上一次GC之后 Heap 的各域分配策略动态变化
针对分代垃圾回收调整部分参数
JVM内存的系统级调优主要目的是减少 Minor GC的频率和 Full GC的次数,过多的 Minor GC 和 Full GC 是会占用很多的系统资源,影响系统的吞吐量。
1. 年轻代分三个区,一个 Eden区,两个 Survivor区(from 和 to),可以通过 --XX:SurvivorRatio 调整比例。
默认 --XX:SurvivorRatio=8,表示 Survivor区与 Eden区的大小比值是 1:1:8,在 MinorGC过程中,如果 survivor 空间不够大,不能够存储所有从 eden 空间和 from survivor 空间复制过来的活动对象,溢出的对象会被复制到 old代,溢出迁移到 old代,会导致 old 代的空间快速增长。
2. 大部分对象先在 Eden 区中申请内存
可以通过设置 -XX:PreTenureSizeThreshold 大小,令大于这个值的对象直接保存到年老代,避免在 Eden区和 Survivor 区之间频繁地通过复制算法回收内存。
3. 当 Eden区满时,无法为新的对象分配内存时,会进行 Minor GC对其回收无用对象占用的内存,如果还有存活对象,则将存活的对象复制到 Survivor From;从 Eden区存活下来的对象,就会被复制到 To区,经历一定的次数 Minor GC后,还存活的对象,将被复制到“年老区(Tenured)”
Minor默认15次,可通过 --XX:MaxTenuringThreshold参数调整年轻代回收次数,防止对象过早进入年老代,降低老年代溢出的可能性。
4. 年轻代和年老代的默认比例是 1:2 ,即年轻代占堆内存的 1/3,年老代占 2/3,可调整 -XX:NewRatio 的大小设置年轻和年老的比例。
默认 --XX:NewRatio=2,即 young:tenured=1:2,适当调整年轻代大小,可以一定程度上减少 Full GC出现概率。
其余性能调优常用参数设置
1. -Xms and -Xmx(or:--XX:InitialHeapSize and --XX:MaxHeapSize):指定 JVM的初始和最大堆内存大小,两值可以设置相同,以避免每次垃圾回收后 JVM重新分配内存。
2. -Xmn:设置年轻代大小。整个堆大小 = 年轻代 + 年老代 + 持久代大小。所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的 3/8。
3. -Xss:设置每个线程的堆栈大小。JDK5.0 以后每个线程堆栈大小为1M。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000-5000左右。
4. -XX:+HeapDumpOnOutOfMemoryError and --XX:HeapDumpPath:让JVM在发生内存溢出时自动生成堆内存快照(堆内存快照文件有可能很庞大,推荐将堆内存快照生成路径指定到一个拥有足够磁盘空间的地方)
5. --XX: OnOutOfMemoryError:当内存溢出发生时,我们可以执行一些指令,比如发个 E-mail通知管理员或者执行一些清理工作。
6. --XX:PermSize and --XX:MaxPermSize:设置永久代大小的初始值和最大值。
默认最小值为物理内存的 1/64,最大值为物理内存的 1/16,永久代在堆内存中是一块独立的区域,这里设置的永久代大小并不会被包括在使用参数 --XX:MaxHeapSize 设置的堆内存大小中。
7. --XX:PretenureSizeThreshold:令大于这个设置值的对象直接在老年代中分配。这样做的目的是避免在 Eden区及两个 Survivor区之间发生大量的内存复制。
java内存模型和垃圾回收的更多相关文章
- Java内存模型与垃圾回收
1.Java内存模型 Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法 ...
- java内存模型和垃圾回收(收藏)
java内存模型: https://www.cnblogs.com/handsomeye/p/5442879.html java垃圾回收 http://www.cnblogs.com/handsome ...
- JAVA内存模型及垃圾回收自我总结
本文为原创,根据<深入理解java虚拟机>和自己的一些理解进行整理,单纯和看其他人的博客感觉不如自己一点点的画和记录来的印象深刻. JAVA内存模型: 上图中:局部变量表所需的内存在编译期 ...
- Java内存模型与垃圾回收笔记
内存模型 栈. 局部变量(基本类型)与对象引用:线程隔离.每个方法执行时会创建一个栈帧,存储局部变量等. 堆. 对象实例:线程共享. 方法区.类信息.常量(final).静态变量.符号引用: 线程共享 ...
- Java虚拟机内存模型及垃圾回收监控调优
Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...
- 【Java_基础】JVM内存模型与垃圾回收机制
1. JVM内存模型 Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间. JVM内存模型如下图所示 1.1 程序计数器 程序计数器( ...
- 程序猿的日常——JVM内存模型与垃圾回收
Java开发有个很基础的问题,虽然我们平时接触的不多,但是了解它却成为Java开发的必备基础--这就是JVM.在C++中我们需要手动申请内存然后释放内存,否则就会出现对象已经不再使用内存却仍被占用的情 ...
- JVM内存模型和垃圾回收
Java开发有个很基础的问题,虽然我们平时接触的不多,但是了解它却成为Java开发的必备基础——这就是JVM.在C++中我们需要手动申请内存然后释放内存,否则就会出现对象已经不再使用内存却仍被占用的情 ...
- (转载)JVM中的内存模型与垃圾回收
转载自微信公众号:Java高级架构(Java-jiagou)-----看完这篇文章,我奶奶都知道JVM中的内存模型与垃圾回收了! 六.内存模型 6.1 内存模型与运行时数据区 Java虚拟机在执行J ...
随机推荐
- 内存Zone中的pageset成员分析
1: struct per_cpu_pageset __percpu *pageset; 首先,分析一个函数,__free_pages,这个函数是Buddy System提供的API接口函数,用于翻译 ...
- 剑指offer——55两个链表的第一个公共节点
题目描述 输入两个链表,找出它们的第一个公共结点. 题解: 分别遍历两个链表到链尾,并计算其长度,若最后一个节点相同,则存在公共节点 然后让长链表指针从头先移动长度差个节点,然后两个链表指针一起移动, ...
- Spring Boot Restful WebAPI集成 OAuth2
系统采用前后端分离的架构,采用OAuth2协议是很自然的事情. 下面开始实战,主要依赖以下两个组件: <dependency> <groupId>org.springframe ...
- 关于VS的第一次使用
参考链接:https://blog.csdn.net/qq_36556893/article/details/88605617
- C# 编译生成 产生多余的语言包删除"de" "en" "es" "fr" "hu" "it" "ja" "ko" "pr-br" "ro" "pt-br" "ru" "sv" "zh-hans" "zh-hant&qu
VS生成事件 rd /s /q "de" "en" "es" "fr" "hu" "it& ...
- python中 try、except、finally执行顺序
我们虽然经常用到try...except 作为异常补货,但是其实很少去研究try源码和机制,也许点进去看过,也是看不出个所以然来 class Exception(BaseException): &qu ...
- C语言typedef
#include <stdio.h> //基本类型 typedef int MyInt; //可以对typedef产生的类型名二次起别名 typedef MyInt MyInt2; // ...
- 关于使用AWS的centos
AWS的centos在版本上有些许不同. 当使用6代的时候,默认的登录用户是root 使用7代的系统,默认的登录用户是centos 否则登录不上去
- redis设置自动启动
按照如下操作即可(可以自定义目录) mkdir /redis cd /redis wget http://download.redis.io/releases/redis-4.0.1.tar.gz t ...
- this.$router.push
跳转详情页this.$router.push({ path: `/activityDetails/${id}` })