十分钟讲清楚java 内存分配,及异常与解决方案,并内存原理

标签: java 内存 异常 | 发表时间:2015-05-19 00:54 | 作者:neo_it
出处:http://www.iteye.com

        讲道理前,先讲解决各种OOM(out of memory)办法,速成:

        StackOverflowError :通常因为递归函数引起(死递归,递归太深)。-Xss 128k 一般够用。

        out Of memory: PermGen Space:通常是动态类大多,比如web 服务器自动更新部署时引起。-Xmx 256M,一般够用。JDK 8 没有PermGen Space,相对应是MetaSpace

        OutOfMemoryError:unable to create native thread : 线程数太多(查看下线程数)或者给虚拟机内存过大( -Xmx 值小点)

       out Of memory :heap space  : 没有及时释放对象,主要查下各类集合引用的对象。这类问题最难查,可借且 jvisualvm 程序,仔细分析

      

       下面,开始讲道理

       遇到内存问题,大多数程序员会觉得头痛,看到报内存异常问题不知怎么办,觉得太多专业术语。所以,我打算用最用最简洁的话,解释清楚内存问题。

       从开头讲起,计算机在运行程序的时候,都是一条条指令的运行,那么运行到哪里了,总得有个标记,这个标记就是放在程序计数寄存器中。

      不管我们执行的是进程还是线程,最终要执行的都是一个个的方法(函数 function)。方法有传入的参数,还有传出的参数,执行过程中,还有变量,那么总得给这个方法一块内存,我们叫这块内存为栈(stack),这块内存在方法执行完后,就没必要存在了,就会清理掉,所以你也可以理解成“暂” 时的。

      方法运行的时候,会生成些结果,有些结果占用的内存比较大,不适合用参数来传递,而且这个结果可能要保留很久,所以需要一个大块的内存,叫“堆”(heap),可以想象成一个大仓库,总是要堆些东西。在 c 里面用 alloc() 分配,free()还释放, java里用 new 来分配。

      在java 中,还有个叫本地栈,专门给调用native 程序用的,比如win32的代码。

     

      好了,基本概念就如上,很简单。下面讲内存释放问题。

      计算机的内存总是有限的,所以,使用完一块内存,最好马上释放掉,给其它程序用。使用很简单,new 一下,一块内存就被用了。但释放就没那么简单了,因为不知道谁该来释放。打个比方,就象超市的塑料袋,厂家生产出来后,给超市,超市又给顾客,那顾客用完后是不是会妥善的把塑料袋处理掉?扔在阴沟里的塑料袋得怎么处理?

      对C/C++程序,必须仔细规划程序结构,及时的调用free()。不过,现实情况就象塑料袋一样,free()函数有时候还真不知道放哪里最合适,即保证需要的人还能用,又要确保没有人用的时候要销毁。

     

       所以java就想到自动回收内存机制

       回收的原理就象有辆垃圾车,整天都在城市里转,发现垃圾就收回来。

       首先,垃圾车得发现垃圾,基本的就是 计数法,如果某个对象 A,被B使用了,计数值就加1,不被使用了就减1。如果计数值是0,那么就回收。这个叫 计数/清理法  。 

       计数法会出现问题是,如果A引用B,B引用A,但A和B都不被主程序引用,那么A和B应该都被清理,但计数值又不是0,所以无法清理,针对这个问题,就产生了标记法。标记法从主程序的对象(root obecjt)开始找,如果某个对象(比如A)被主程序引用,那就是活的,该对象又引用其它的(比如B),那么B也是活的。 如果C对象不被A和B引用,那么C就是死的。i活与死都做个标记,这就叫标记法 

       发现之后,就得清理,怎么清理呢?最简单的就是把内存标记为未用。这叫清理法

       简单的清理,会引起内存碎片,比如家里的冰箱,一个个格子里都塞些小零碎,现在要放整个西瓜进去,怎么办? 那就要整理,把小块的碎片集中放,腾出地方放西瓜。这叫整理法。

       如果整理东西的时候,地方有限,那小块的碎片得左腾右挪的,效率比较低,所以,最好是有块空的场地,和原来一样大,那整理起来顺手多了,所以就搞两块内存,一块内存是当前使用的,另一块内存是整理的时候用的,整理的时候把当前用的复制过去,这叫做 复制法。

        简单的复制法有个问题,内存有一半平时不用,比较浪费。经过研究发现,有大量对象生命期很短,也就是说要复制的内存其实不大。所以java 把当前使用的分配得大些,叫eden区,第一次复制的时候把eden复制到一个小块的区域,叫 survival 区。再下一次的时候,检查eden区和survival区还活着的对象,再复制到第二个survival区。所以,survial区有两块。

       这样做了后,还有个问题,有些对象要活很久,总是在 survival区复制来复制去,浪费时间,所以复制到一定次数后,被判断为长期有效对象,于是就另外再开辟一块内存,叫它老年代内存,把老年对象复制到这里去。而eden和survival叫新生代区。年老代的内存也会被清理,只是不象新生代区那么频繁,而且不会采用复制法,只采用整理法,因为没多余的内存。

        另外,java是个动态语言,运行时可以拿到类的定义信息,还可以产生动态的类,所以需要一个区域专门放类的定义 ,在JDK 8之前 叫永久代区 permanent generation ,永久代区的信息很难清理,因为不知道某个类是不是会再次被用到。而且最初sun 也只是设置了较小的区域给永久代,因为认为类在程序启动的时候都基本被确定了,不需要很大,但是因为现在的程序大量使用到动态特性,比如web程序中的jsp,就是会被动态编译,各种框架也会用到动态代理机制,所以产生了大量类放到了永久代,不小心就产生了PermGen out of memeory 错误。到了JDK 8,为了解决这个问题,取而代之的是 MetaSpace,其实MetaSpace只是默认下放大的限制,默认是无限。。。

 

       讲到这里,垃圾回收的发现与清理叫完了,接下来得讲怎么执行。也就是垃圾车多久去逛下城市,是只派一辆车吗?还是多派几辆。如果只派一辆,那就是串行执行,如果多派几辆,那么就是并行执行。

       针对 新生代和年老代,是有不同的执行者,采用的策略也有点不一样。

       

       下面再给JVM 的参数

垃圾搜集器选择参数

 

           UseSerialGC:开启此参数使用serial & serial old搜集器(client模式默认值)。

           UseParNewGC:开启此参数使用ParNew & serial old搜集器(不推荐)。

           UseConcMarkSweepGC:开启此参数使用ParNew & CMS(serial old为替补)搜集器。

           UseParallelGC:开启此参数使用parallel scavenge & parallel old搜集器(server模式默认值)。

           UseParallelOldGC:开启此参数在年老代使用parallel old搜集器(该参数在JDK1.5之后已无用)。

 

JVM各个内存区域大小相关参数

 

           Xms:堆的初始值。默认为物理内存的1/64,最大不超1G。

           Xmx:堆的最大值。默认为物理内存的1/4,最大不超1G。

           Xmn:新生代的大小。

           Xss:线程栈大小。

           PermSize:永久代初始大小。默认为物理内存的1/64,最大不超1G。

           MaxPermSize:永久代最大值。默认为物理内存的1/4,最大不超1G。

           NewRatio:新生代与年老代的比例。比如为3,则新生代占堆的1/4,年老代占3/4。

           SurvivorRatio:新生代中调整eden区与survivor区的比例,默认为8,即eden区为80%的大小,两个survivor分别为10%的大小。(备注:这个参数设定是讲解复制算法那一章中,解决复制算法内存减半的办法。eden区即是复制算法一章中80%的那部分,而survivor区则是两个10%的那部分。)

 

垃圾搜集器性能 通用参数

          

           PretenureSizeThreshold:晋升年老代的对象大小。默认为0,比如设为10M,则超过10M的对象将不在eden区分配,而直接进入年老代。

           MaxTenuringThreshold:晋升老年代的最大年龄。默认为15,比如设为10,则对象在10次普通GC后将会被放入年老代。

           DisableExplicitGC:禁用System.gc()。

 

并行搜集器参数

 

           ParallelGCThreads:回收时开启的线程数。默认与CPU个数相等。

           GCTimeRatio:设置系统的吞吐量。比如设为99,则GC时间比为1/1+99=1%,也就是要求吞吐量为99%。若无法满足会缩小新生代大小。

           MaxGCPauseMillis:设置垃圾回收的最大停顿时间。若无法满足设置值,则会优先缩小新生代大小,仍无法满足的话则会牺牲吞吐量。

          

并发搜集器参数

 

           CMSInitiatingOccupancyFraction:触发CMS收集器的内存比例。比如60%的意思就是说,当内存达到60%,就会开始进行CMS并发收集。

           UseCMSCompactAtFullCollection:这个前面已经提过,用于在每一次CMS收集器清理垃圾后送一次内存整理。

 

           CMSFullGCsBeforeCompaction:设置在几次CMS垃圾收集后,触发一次内存整理。

 

 

        

            

 

    



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 内存 异常] 推荐:

Java异常

- - CSDN博客推荐文章
“好的程序设计语言能够帮助程序员写出好程序,但是无论哪种语言都避免不了程序员写出坏的程序.                                                                                                                          ----《Java编程思想》.

浅谈java异常

- - 移动开发 - ITeye博客
在《java编程思想》中这样定义 异常:阻止当前方法或作用域继续执行的问题. 虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常. 绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败. 之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意.

十分钟讲清楚java 内存分配,及异常与解决方案,并内存原理

- - 编程语言 - ITeye博客
        讲道理前,先讲解决各种OOM(out of memory)办法,速成:.         StackOverflowError :通常因为递归函数引起(死递归,递归太深).         out Of memory: PermGen Space:通常是动态类大多,比如web 服务器自动更新部署时引起.

JAVA内存释放

- - Java - 编程语言 - ITeye博客
(问题一:什么叫垃圾回收机制. ) 垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能. 当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露. (问题二:java的垃圾回收有什么特点. ) JAVA语言不允许程序员直接控制内存空间的使用.

Java 堆内存(Heap)

- - ITeye博客
        堆(Heap)又被称为:优先队列(Priority Queue),是计算机科学中一类特殊的数据结构的统称. 堆通常是一个可以被看做一棵树的数组对象. 在队列中,调度程序反复提取队列中第一个作业并运行,因而实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权.

java内存泄漏

- - 编程语言 - ITeye博客
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址. Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的. GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.

Java异常处理策略

- - 研发管理 - ITeye博客
任务与预先设定的规则不相符的情况都可以称之为异常. 但凡业务逻辑操作,都会划定一些边界或规则,但是往往事与愿违,总会有调皮鬼来挑战系统的健壮性. 用户并不都是知道潜规则的,比如用户的银行账户中只有100块钱,但是用户不查询就直接取200块. 码农有时候太过自信了,比如你在编写文件下载功能时忽略了文件有可能不存在这个分支流程.

深入Java内存模型

- - ImportNew
你可以在网上找到一大堆资料让你了解JMM是什么东西,但大多在你看完后仍然会有很多疑问. happen-before是怎么工作的呢. 用volatile会导致缓存的丢弃吗. 为什么我们从一开始就需要内存模型. 通过这篇文章,读者可以学习到足以回答以上所有问题的知识. 它包含两大部分:第一部分是硬件层次的大体架构,第二部分是深入OpenJdk源代码和实现.

Java内存之"栈"与"堆"

- - ITeye博客
        昨天中午,发了一篇 equals和==区别的博文,晚上再看时有几位大牛指出了其中的一些错误,很感谢他们的留言,一句简简单单的留言给了我对这些错误知识点改正的机会. 或许这就是从事互联网行业所提倡的互帮互助的精神吧,因为有分享,有交流,互联网才会发展的如此迅猛. 大牛提的一个观点很好,好的东西可以拿出来分享,错的东西却可能带给别人错误的理解,这一点我确实得向看了我写了一些bug博客的人道个歉.

浅谈Java--内存泄漏

- - ITeye博客
      JAVA的垃圾回收机制,让许多程序员觉得内存管理不是很重要,但是内存内存泄露的事情恰恰这样的疏忽而发生,特别是对于Android开发,内存管理更为重要,养成良好的习惯,有利于避免内存的泄漏..     这里可以把许多对象和引用看成是有向图,顶点可以是对象也可以是引用,引用关系就是有向边.