翻看电脑的文件夹,无意看到了9月份在公司做的一次分享,浏览了一下"婆婆特",发现自己在ppt上的写的引导性问题自己也不能确切的回答出来,哎,知识这东西,平时不常用的没些日子就生疏了。于是,本小白决定把他整理下来,不敢班门弄斧,对于入门的同学可以快速了解虚拟机的大概,有错误的地方请批评指正。

一、java虚拟机的内存结构

方法区:线程共享,存放已被虚拟机加载的常量,静态变量,类信息,即时编译后的代码等数据。
 
前不久有个同事聊天的时候说起他们也做了个关于虚拟机的分享,顺便考考我,他问我Class文件的存放地点,我说不知道,忘了,不好瞎猜,他说在永久代,我当时将信将疑。查了一下书籍和相关资料,很多人把hotSpot虚拟机的方法区叫做永久代。对于其他虚拟机如BEA JRockit,IBM J9根本不存在永久带的概念。所以这样说其实很不精确,恐怕他自己也只是记住了永久代这个词,但并不知道永久代指什么。这给我的启示是不要被别人一知半解,似对似错的知识信以为真。也不要不是特别明白就加个“好像”无意识的忽悠了别人。更不要被忽悠后不去查证,下次谈论起来,人家如果反驳,自己也会犹豫。
 
在后面讲到的java堆,可以看到堆分为新生代,老年代,和永久代(方法区),跟上图有冲突(上图中方法区与永久代是分开的),这是因为java虚拟机把他作为堆的一个逻辑部分,但实际上他叫Non-Heap(非堆),目的是与堆区分开来。再细说就是方法区有一个运行时常量池,专门存放编译器生成的字面量和符号引用。
 
虚拟机栈:虚拟机栈和程序计数器一样是程序私有的,程序计数器下面会讲到。它和线程的生命周期相同。每个方法被执行时都会创建一个新的栈帧,用于存储本地变量,动态链接,方法出口,等信息。每一个方法调用直至执行完成就对应着一个栈帧从入栈到出栈的过程。通常所说的堆栈信息中的栈即是java虚拟机栈。这个内存区会出现OOM异常
 
本地方法栈:本地方法栈与java虚拟机栈相似,但本地方法栈中执行的是本地方法, 啊?啥叫本地方法?就是通过该方法java程序可以调用非java程序写的方法,这也是java可以跨平台的一个重要原因,它屏蔽了一个普通程序员对底层的操作的繁琐,比如java 多线程中存在一个方法可以调整线程优先级,其实在windows平台下,它调用的是操作系统的方法,其他的平台它则调用其它平台相关的方法。具体的怎样写一个本地方法,怎样去编译,网上搜一搜就会有完整的例子,如同“hello world” 一样简单。
 
程序计数器:是一小块内存,当前线程所执行字节码的行号指示器,字节码解释器就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要计数器来完成,这是java运行时区唯一没有定义OutOfMemory方法的区域。
 
堆:堆是java内存中最大的一块区域,是被所有线程所共享的一块区域。几乎所有的创建的对象都分配在堆。java堆分为新生代和老年代,细分为Eden空间,From Survior空间和to Survior空间
 
2)对象是如何进行访问的?
对象访问有两种方式:
方式一:使用句柄的访问方式
 
 
 句柄的访问方式,java堆中会划分一块内存作为句柄池,java虚拟机栈帧中的引用对象存储的是句柄池中的引用地址,句柄池中存放着到实例对象的指针和到对象类型数据的指针分别指向堆中的实例,和方法区中对象类型数据。对象的类型数据指对象实现的接口,继承的父类,对象的类型等
 
方式二:直接指针的方式
 
直接指针的方式:虚拟机栈帧中存储的是对象实例的地址,而到对象类型数据的指针则存放在对象的实例数据中
 
两种对象访问方式的比较:
1使用句柄的方式,不论对象的实例数据还是类型数据都存在堆中的句柄池,当对象实例位置改变(垃圾回收时经常发生)则需要修改指向实例的指针,修改句柄池中的引用,而虚拟机栈中的引用则不用修改
2从图中可以看出,图一使用3个指针,图二使用2个指针,明显少了一次寻址,所以它的明显优势就是访问速度快,减少了一次指针定位的时间开销,程序执行时对象的访问非常频繁,微小的开销将变得非常可观。HotSpot虚拟机采用的是第二种访问方式。
 
二、java中的垃圾回收策略
首先要问几个问题,如何判断一个对象已经死亡?是否对象只要被引用就没有死亡就不会被回收?什么是根搜索算法?
请看下面一段程序:

public class ReferenceCountGc {

public Object field = null;

public static void main(String[] args) {

ReferenceCountGc objA = new ReferenceCountGc();

ReferenceCountGc objB = new ReferenceCountGc();

objA.field = objB;

objB.field = objA;

objA = null;

objB = null;

System.gc();

}

}

当程序执行到System.gc()时,是否会回收对象objA和objB?

引用计数方法:给对象一个计数器,当对象被引用时就+1,释放掉引用-1,当为0时及不会再被引用。但引用计数方法的Bug是无法解决对象循环引用的问题,但并不是此算法没有用武之地,在很多场景下会使用到这个算法。但java的垃圾回收并没有使用。上面的程序如果使用的是引用计数算法则不会被回收,但虚拟机却使用根搜索算法。

根搜索算法即设定一个对象称为GC root ,从这个节点向下进行搜索,搜索所走过的路径称为引用链,当GC root没有任何引用链相连即在图论中不可到达,则证明此对象不可用

由此来看看,对象死后,垃圾回收算法;

标记-清除算法                                  复制算法
         

灰色矩形框为可回收对象,标记-清除算法就是把可回收的对象进行标记,标记到一定次数则清除掉。从图中可以明显看出,该算法的弊端是会产生大量的磁盘碎片,没有一整片连续的空间,当遇到占用连续的内存空间较多的对象时,由于内存放不下该对象,会提前进行垃圾回收,致使虚拟机垃圾回收频繁,影响性能

为了规避标记清除算法的弊端,出现了复制算法,复制算法将内存一份为二,垃圾回收时将使用的内存中的存活对象,拷贝到另一半内存中,然后把左侧内存区域完全清除掉,上图只是演示了复制算法,但并非一分为二,使用和保留的空间是1:1,可以根据实际情况对虚拟机参数进行调整。此算法的弊端是要保留内存空间,会将可用内存变少。

标记整理算法:                              分代收集算法:

     

标记整理算法:绿色和蓝色区域都代表存活对象,当进行垃圾回收时把存活对象依次移到最左边,移动后将其余内存空间清空。

分代收集算法:如图,其实就是没有算法。。。把以上3种算法进行综合运用,前面说过堆是有划分的,简单分为新生代和老年代,分代收集就是根据不同代的特点应用不同的垃圾回收算法。

三、java内存分配

java的自动内存管理解决了两个问题,一是给对象分配内存,二是回收分配给对象的内存,前面我们讲了回收分配给对象的内存,下面我们来看看给对象分配内存

java堆分为新生代,老年代(终身代)和永久代。

新生代和老年代的默认比值为1:2即新生代占堆总内存的1/3,可以通过 –Xms(初始堆大小)、-Xmx (最大堆大小)来改变。

新生代又分为Eden区、From survivor区、To survivor区,Eden:From:To 为 8:1:1,可以通过–XX:SurvivorRatio 参数设定。

JVM每次只会使用From区和survivor区中的一块(Form survivor或To survivor),很明显是为了在垃圾回收的时候将存活对象移到另外一个空闲的survivor区(如果空间足够,否则直接进入老年代),因此垃圾回收所使用的算法是复制算法。

新生的对象会被分配到新生代,新生代的特点是朝生夕死,对象存活的时间短,迭代快。发生在新生代的垃圾回收叫minor GC,minor Gc进行的相对频繁,消耗较full GC少,而Full GC是发生在老年代的垃圾回收,采用的是标记-清除算法。老年代进行一次垃圾回收比新生代话费时间长,进行的也没有老年代频繁,同时要尽量减少老年代的垃圾回收,因为回收速度慢且在进行时影响虚拟机性能,使虚拟机响应变慢,最直接的感觉是应用程序的响应速度变慢。

什么情况下,对象会被分配到老年代?

从上图中明显可以到3点:

大对象:什么是大对象,大对象就是需要连续占用很多内存空间的对象比如很长的字符串和数组。虚拟机通过-XX:PretrnureSizeThreshold设计大过指定大小的对象之间进入老年代,即时没有超过指定大小,在进行minor GC时通常也会因为survivor区不够用而被转移到老年代。

通过设置MaxTenuringThreshold参数: 这个参数是进行年龄设置的,超过这个年龄的会进入老年代。什么是年龄?在新生代进行minor GC的时候,每进行一次,存活下来的对象年龄+1,默认年龄超过15的会进入老年代。15这个数值也可以通过MaxTenuringThreshold参数改变。

Survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,所有大于或者等于该年龄的对象直接进入老年代,这句话比较拗口,但就是这个意思,看不懂的多看几遍就好了。。。

前面讲了这么多参数,如何设置虚拟机参数?可以通过IDE进行设置,不论是调整空间大小,还是设置对象的年龄进入老年代,如下图

三、我们开发中应注意的问题

从虚拟机上可以看出,主要是避免full GC的次数,减少朝生夕死的大对象,对虚拟机内存进行优化,在日常开发中写程序的主要注意的是

不要使用长字符串 如:String x = new String(“XXXXXXXXXXXX”) StringBuffer stringBuffer = new StringBuffer()StringBuffer对象的append()方法。当然,考虑到线程安全问题,使用StringBuilder.

http://www.cnblogs.com/jarman/p/5042036.html

JVM内存结构、垃圾回收那点事(转)的更多相关文章

  1. Java进阶 JVM 内存与垃圾回收篇(一)

    JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...

  2. Java内存与垃圾回收调优

     Java(JVM)内存模型 正如你从上面的图片看到的,JVM内存被分成多个独立的部分.广泛地说,JVM堆内存被分为两部分——年轻代(Young Generation)和老年代(Old Generat ...

  3. 【转】Java内存与垃圾回收调优

    要了解Java垃圾收集机制,先理解JVM内存模式是非常重要的.今天我们将会了解JVM内存的各个部分.如何监控以及垃圾收集调优. Java(JVM)内存模型 正如你从上面的图片看到的,JVM内存被分成多 ...

  4. 面试~jvm(JVM内存结构、类加载、双亲委派机制、对象分配,了解垃圾回收)

    一.JVM内存结构 ▷ 谈及内存结构各个部分的数据交互过程:还可以再谈及生命周期.数据共享:是否GC.是否OOM 答:jvm 内存结构包括程序计数器.虚拟机栈.本地方法栈.堆.方法区:它是字节码运行时 ...

  5. 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)

    欢迎一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇 > <实战演练,拒绝996篇 > 欢迎关注我博客 也欢迎关注公 众 号[Ccww笔记],原创技术文章 ...

  6. 【转】jvm内存结构

    JVM的基本结构 包括四部分:类加载器.执行引擎.内存区(运行时数据区).本地方法接口 类加载器:jvm启动时或类运行时将需要的class文件加载到JVM中. JVM内存申请过程如下: JVM 会试图 ...

  7. 【JVM】JVM系列之垃圾回收(二)

    一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...

  8. JVM内存结构之三--持久代

    本文会介绍一些JVM内存结构的基本概念,然后很快会讲到持久代,来看下Java SE 8发布后它究竟到哪去了. 基础知识 JVM只不过是运行在你系统上的另一个进程而已,这一切的魔法始于一个java命令. ...

  9. 最简单例子图解JVM内存分配和回收

    一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...

  10. JVM内存结构

    前言 在Java语言开发过程中,out of memory错误是很常见的一种错误.对于JVM的内存结构有更深入的了解,更更好的帮我们排查此类问题,有效的避免此类问题发生.在JAVA 8中内存结构有进行 ...

随机推荐

  1. Java中对不变的 data和object reference 使用 final

    Java中对不变的 data和object reference 使用 final 许多语言都提供常量数据的概念,用来表示那些既不会改变也不能改变的数据,java关键词final用来表示常量数据.例如: ...

  2. HDU2037 今年暑假不AC 【贪心】

    今年暑假不AC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  3. FMOD在Android玩音响系统的抖动问题

    1. 基本介绍 在Android升级系统Android4.4之后,发现FMOD在Android音会出现抖动.导致声音不正常.边赫赫有名的"极品飞车"都有问题. 经查验,是FMOD的 ...

  4. HttpApplication处理对象与HttpModule处理模块

    HttpApplication处理对象与HttpModule处理模块 (第三篇) 一.HttpApplication对象简述 在HttpRuntime创建了HttpContext对象之后,HttpRu ...

  5. 设计模式学习一:strategyPattern

    #ifndef STRATEGYPATTERN_H_#define STRATEGYPATTERN_H_#include<iostream>using namespace std; //策 ...

  6. Android中Dialog的使用

    上一篇博文讲到对话框popWindow的使用,这篇博文主要解说Dialog的使用. 1.什么是Dialog? Dialog就是对话框的一种方式! 在Android开发中.我们常常会须要在Android ...

  7. Just like normal variables,

    Just like normal variables, pointers can be declared constant. There are two different ways that poi ...

  8. 第1周 SQL Server 如何执行一个查询

    原文:第1周 SQL Server 如何执行一个查询 大家好,欢迎来到第1周的SQL Server性能调优培训.在我们进入SQL Server性能调优里枯燥难懂的细节内容之前,我想通过讲解SQL Se ...

  9. Ubuntu 12.04开启3D桌面特效

    1.设定软件源,更新软件 点击左边栏Dash主页(Ubuntu图标),输入更新管理器,会出现更新管理器,打开后点设置,弹出软件源对话框,为确保能够正常更新,选主服务器 点击检查,更新完后,点重启 2. ...

  10. 配置Tomcat的日志系统

    成功配置tomcat的log4j日志系统,格式:HTML+每天以yyyy-mm-dd.log命名的日志文件 一.引言: 实习单位让用log4j配置webapp的日志系统,要求产生的日志文件是html格 ...