Jvm 内存浅析 及 GC个人学习总结
从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言。这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序。今天我们就来说说Java的一项基本但非常重要的技术内存管理
了解C语言的同学都知道,在C语言中内存的开辟和释放都是由我们自己来管理的,每一个new操作都要对于一个delete操作,否则就会参数内存泄漏和溢出的问题,导致非常槽糕的后果。但在Java开发过程中,则完全不需要担心这个问题。因为jvm提供了自动内存管理的机制。内存管理的工作由jvm帮我们完成。这样我们就不用为了释放内存而头疼了。
Jvm内存浅析
虽然jvm帮我们做了内存管理的工作,但是我们仍需要了解jvm到底做了什么,下面我们就一起去看一看
jvm启动时进行一系列的工作,其中一项就是开辟一块运行时内存。而这一块内存中又分为了五大区域,分别用于不同的功能。
程序计数器
记录程序运行的下一条指令的地址,这里的“地址”可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时程序计数器的值为”undefined”.在多线程环境下,每一个线程都有自己的程序计数器,在jvm调度线程时,会把当前的线程的程序计数器保存到快照,以便下次线程获取执行时间时获取
VM Stack
虚拟机栈是Java方法执行的内存模型,每个方法执行的时候,会在栈中创建一帧用于存储局部变量表、操作数栈、动态链接、方法出口。方法开始调用时,会创建栈帧并入栈,方法执行结束时会出栈。每个线程都有自己的栈。
动态链接:是一种在常量池中指向方法的符号引用,需要在运行期确定为直接引用
方法出口:当前执行方法的调用者的程序计数器,或异常处理表的地址
可以通过 -xxs 大小 来配置栈的大小,当嵌套调用使用不当,会导致方法不停的入栈,最终导致栈空间被占满产生 StackOverflowError
本地方法栈
Heap
堆是用于存放对象实例的地方,几乎所有对象实例在堆中分配。堆是线程共享的,这是多线程时同步机制的原因。
堆是GC管理的主要区域,GC在对堆进行回收前,首先要确定对象是否已死(不可能再被使用的对象)
判断对象是否存活的算法有两种:引用计数算法、可达性分析算法
引用计数算法是为每一个对象添加一个引用计数器,每当有一个引用指向它时,计数器就加一,任何时刻计数器为0的对象就不可能再被使用。这种算法实现简单,但是它很难解决对象循环引用的问题(何为循环引用见下方备注)
可达性分析算法是Java语言正在使用的算法。它的基本思想是通过一系统被称为“GC Root”的对象为起点,从这个起点向下搜索,搜索走过的路径称为引用链,当一个对象不再任何引用链上时,则说明这个对象是不可能再被使用的。
在Java语言中,GC Root包括以下几种对象:
- 虚拟机栈中引用的对象
- 本地方法栈中JNI引用的对象
- 方法区中类静态成员变量引用的对象
- 方法区中常量引用的对象
可以看出分析对象是否存活,都与引用有关。在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为 强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)
- 强引用
强引用即为原来意义上的引用,只要强引用存在,被引用的对象就不会被回收
- 软引用
SoftReference类表示软引用,对于被软引用关联的对象,在系统将要发生内存溢出时,会把这些对象列入回收范围后,进行二次回收
- 弱引用
WeakReference类表示弱引用,对于被弱引用关联的对象,只能生存到下一次垃圾回收发生之前
- 虚引用
PhantomReference类表示虚引用,虚引用不对关联的对象的生存时间构成影响,也无法取得对象实例,它唯一的作用是在对象被GC回收是收到一条系统通知
堆得大小可以通过-Xmx和-Xms来控制。对于主流的Jvm,GC基本都采用分代收集的算法。基于这个算法, Java堆又分为新生代(Young Generation)和老年代(Old Generation),新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace和ToSpace组成。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例。老生代用于存放新生代中经过多次垃圾回收(也即Minor GC)仍然存活的对象。
永生代(Permanent Space)为方法区
方法区
方法区也为所以线程所共享,用于存放已加载的类信息、静态变量、常量和即时编译器编译后的代码。-XX:MaxPermSize用于设置方法区大小
直接内存
直接内存不是虚拟机运行时数据区的一部分。通过Native函数库直接分配的堆外内存,然后通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
内存分配和回收策略
目前为止,jvm已经发展处三种比较成熟的垃圾收集算法:1.标记-清除算法;2.复制算法;3.标记-整理算法;4.分代收集算法
1. 标记-清除算法
这种垃圾回收一次回收分为两个阶段:标记、清除。首先标记所有需要回收的对象,在标记完成后回收所有被标记的对象。这种回收算法会产生大量不连续的内存碎片,当要频繁分配一个大对象时,jvm在新生代中找不到足够大的连续的内存块,会导致jvm频繁进行内存回收(目前有机制,对大对象,直接分配到老年代中)
2. 复制算法
这种算法会将内存划分为两个相等的块,每次只使用其中一块。当这块内存不够使用时,就将还存活的对象复制到另一块内存中,然后把这块内存一次清理掉。这样做的效率比较高,也避免了内存碎片。但是这样内存的可使用空间减半,是个不小的损失。
3. 标记-整理算法
这是标记-清除算法的升级版。在完成标记阶段后,不是直接对可回收对象进行清理,而是让存活对象向着一端移动,然后清理掉边界以外的内存
4. 分代收集算法
当前商业虚拟机都采用这种算法。首先根据对象存活周期的不同将内存分为几块即新生代、老年代,然后根据不同年代的特点,采用不同的收集算法。在新生代中,每次垃圾收集时都有大量对象死去,只有少量存活,所以选择了复制算法。而老年代中因为对象存活率比较高,所以采用标记-整理算法(或者标记-清除算法)
GC的执行机制
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge 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调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
1.年老代(Tenured)被写满
2.持久代(Perm)被写满
3.System.gc()被显示调用
4.上一次GC之后Heap的各域分配策略动态变化
Java常见的内存泄漏
- 数据库连接,网络连接,IO连接等没有显示调用close关闭,会导致内存泄露
- 监听器的使用,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露
作者:南唐三少
出处:http://www.cnblogs.com/nantang
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我们最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文链接,否则保留追究法律责任的权利。
Jvm 内存浅析 及 GC个人学习总结的更多相关文章
- 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控
如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...
- JVM内存模型及GC回收算法
该篇博客主要对JVM内存模型以及GC回收算法以自己的理解和认识做以记录. 内存模型 GC垃圾回收 1.内存模型 从上图可以看出,JVM分为 方法区,虚拟机栈,本地方法栈,堆,计数器 5个区域.其中最为 ...
- JVM内存概览与GC初步
一.JVM内存空间概览 Java虚拟机使用的内存块包含 栈空间Stack (虚拟机栈.本地方法栈).堆空间 Heap Memory .永久区 Perm Gen(related to method ar ...
- JVM内存模型与GC算法
1.JVM内存模型 JVM内存模型如上图,需要声明一点,这是<Java虚拟机规范(Java SE 7版)>规定的内容,实际区域由各JVM自己实现,所以可能略有不同.以下对各区域进行简短说明 ...
- JVM内存模型和GC机制
目录 1.JVM内存模型 2.GC 1.JVM内存模型 堆,栈,本地方法栈,方法区,程序计数器 2.GC 新生代收集器:Serial(单线程).ParNew.Parallel Scavenge: 老年 ...
- JVM内存管理及GC机制
一.概述 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露 ...
- JVM内存结构与GC
JVM内存模型总体架构图 程序计数器多线程时,当线程数超过CPU数量或CPU内核数量,线程之间就要根据时间片轮询抢夺CPU时间资源.因此每个线程有要有一个独立的程序计数器,记录下一条要运行的指令.线程 ...
- JVM内存布局及GC知识回顾
注:本文篇幅较长,且需要有一定的java基础,建议各位看官,备好瓜子.饮料.小板凳,摆个让自己舒服的姿势,慢慢细看^_^, 文中所有素材,均来自互联网,本人只是详细梳理了一遍,形成此文. 一.JVM运 ...
- JVM内存模型与GC算法(简介)
JVM内存模型如上图,需要声明一点,这是<Java虚拟机规范(Java SE 7版)>规定的内容,实际区域由各JVM自己实现,所以可能略有不同.以下对各区域进行简短说明. 1.1程序计数器 ...
随机推荐
- 从RPC开始(一)
这是一篇关于纯C++RPC框架的文章.所以,我们先看看,我们有什么? 1.一个什么都能干的C++.(前提是,你什么都干了) 2.原始的Socket接口,还是C API.还得自己去二次封装... 3.C ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
- scanf()中清除输入缓冲区的几种方法归纳
应用场景:我们使用多个scanf()的时候,如果输入缓冲区还有数据的话,那么scanf()就不会询问用户输入,而是直接就将输入缓冲区的内容拿出来用了,这就导致了前面的错误影响到后面的内容,为了隔离这种 ...
- Hyper-V无法文件拖拽解决方案~~~这次用一个取巧的方法架设一个FTP来访问某个磁盘,并方便的读写文件
异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...
- PHP设计模式(五)建造者模式(Builder For PHP)
建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示的设计模式. 设计场景: 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,爱好等信息,才能获得用 ...
- 【干货分享】流程DEMO-合同会审表
流程名: 合同会审表 业务描述: 合同的审批及签订 流程相关文件: 流程包.xml 事务呈批表业务服务.xml 事务呈批表主数据.xml 流程说明: 1.此流程必须先进行事务呈批表流程的配置才可 ...
- Android Socket连接PC出错问题及解决
最近测试问题:Android 通过Socket链接电脑,ip和端口都是正确的,也在同一网段,可android端就是报异常如下: 解决办法:测试电脑的防火墙可能开着,在控制面板把防火墙打开即可.
- Oracle 用Drapper进行like模糊传参查询需要在参数值前后带%符合
Oracle 用Drapper进行like模糊传参查询需要在参数值前后带%符合 string sqlstr="select * from tblname where name like ...
- [LeetCode] Longest Substring with At Least K Repeating Characters 至少有K个重复字符的最长子字符串
Find the length of the longest substring T of a given string (consists of lowercase letters only) su ...
- 关于我 — About Me
个人简介 姓名:周旭龙 关注:.NET开发技术.Web前端技术 邮箱:edisonchou@hotmail.com GitHub: https://github.com/edisonchou 主要经历 ...