Java中我们基本上不会显式地调用分配内存的函数,分配内存和回收内存都由JVM自动完成了。
 
所谓物理内存就是我们通常说的RAM(随机存储器),计算机中还有一个存储单元叫做寄存器,用于存储计算单元执行指定的中间结果。寄存器的大小决定了一次计算可使用的最大数值。
 
不管是在Windows系统还是在Linux系统下,运行程序都要向操作系统先申请内存地址。通常操作系统管理内存的申请空间是按照进程来管理的,每个进程拥有一段独立的地址空间,每个进程之间不会相互重合,操作系统也会保证每个进程只能访问自己的内存空间(逻辑间独立,操作系统保证)。随着程序越来越庞大和设计的多任务性,物理内存无法满足程序需要,就有了虚拟内存的出现。
 
一个计算机有一定的内存空间,程序并不能完全使用这些地址空间,因为这些地址空间被划分为内核空间和用户空间。内核空间主要是指操作系统运行时所使用的用户程序调度,虚拟内存的使用或者连接硬件资源等的程序逻辑,为了保证操作系统运行的稳定性,运行在操作系统中的用户程序不能访问操作系统所使用的内存空间。
 
Java中哪些组件需要使用内存
 
1. Java堆
 
Java堆用于存储Java对象的内存区域,堆的大小在JVM启动时就一次向操作系统申请完成,通过-Xmx和-Xms两个选项来控制大小。一旦分配完成,堆的大小就将会固定,不能在内存不够时再向操作系统重新申请内存,同时当内存空闲时也不能将多余的空间还给操作系统。
 
2. 线程
 
JVM运行实际程序的实体是线程,线程同时也需要一定的内存空间来存储一些必要的数据,每个线程创建时JVM都会为它创建一个堆栈,堆栈的大小根据不同的JVM实现而不同,通常在256K至768K之间。
 
3. 类和类加载器
 
Java中的类核加载类的类加载器本身同样需要存储空间,在Sun JDK中也被存储在堆中,这个区域叫做永久代(PermGen)。
 
JVM是按需来加载类的,JVM只会加载那些在你的应用程序中明确使用的类到内存中,如果需要查看JVM到底加载了哪些类,可以在启动参数中加上-verbose:class。
 
通常一个类能够被卸载,有如下条件需要被满足:
 
  • 在Java堆中没有对表示该类加载器的java.lang.ClassLoader对象的引用;
  • Java堆中没有对表示类加载器加载的类的任何java.lang.Class对象的引用;
  • 在Java堆上该类加载器加载的任何类的所有对象不再存活(被引用);
 
注意JVM所创建的3个默认类加载器Bootstrap ClassLoader, ExtClassLoader, AppClassLoader都不可能满足这些条件,因此任何系统类或通过应用程序类加载器加载的任何应用程序类都不可能在运行时释放。
 
4. Java NIO
 
Java在1.4版本中添加了NIO相关类库,引入了一种基于通道(Channel)和缓冲区(Buffer)来执行IO的新方式。NIO使用ByteBuffer.allocateDirect()方式分配内存,这种方式也就是通常所说的NIO direct memory,其分配的内存使用的是本机内存而不是Java堆上的内存,这也进一步说明每次分配内存会调用操作系统的os::malloc()函数。
 
直接ByteBuffer对象会自动清理本机缓冲区,但此过程只能作为Java堆GC的一部分来执行,它们不会自动响应施加在本机堆上的压力。GC仅在Java堆被占满,以至于无法为堆分配请求提供服务时发生。当前在很多NIO框架中都在代码中显式地调用System.gc()来释放NIO持有的内存,但这样会影响应用程序的性能,增加GC的次数。如果设置-XX: +DisableExplicitGC来控制System.gc()的影响,但是又会导致NIO direct memory内存泄漏的问题。
 
5. JNI
 
JNI技术使得本机代码可以调用Java方法,也就是通常所说的native memory,实际上Java运行本身也依赖JNI代码来实现类库的功能,如文件操作,网络IO,JNI也会增加Java运行时的本机内存占用。
 
Java内存问题分析
 
有时候Java内存溢出发生时,我们并不知道原因,在JVM启动时可以加上一些参数来控制,当JVM出问题能够记下一些当时的情况,就是记录下来的GC日志,可以观察GC的频度以及每次GC都回收了哪些内存。
 
1. GC的日志
 
日志输出有大概下面的参数:
 
 
-verbose:gc  输出一些详细的GC信息
-XX:+PrintGCDetails 输出GC的详细信息
-XX:+PrintGCApplicationStoppedTime 输出GC造成应用程序暂停时间
-XX:+PrintGCDateStamps GC发生的时间信息
-XX:+PrintHeapAtGC GC前后输出堆中各个区域的大小
-Xloggc:[file] 将GC信息输出到单独的文件中
 
曾经分析过GC的日志,具体过程可以参考:http://brandnewuser.iteye.com/blog/2101501
 
可以根据日志来判断是否有内存在泄漏,如果在每次GC执行完成后,进入Old区或者Perm区的大小,ending occupancy的值一直在增长,而且GC非常频繁,很可能就是内存泄漏。
 
除去日志文件分析后,可以直接通过JVM自带的一些工具分析,如jstat -gcutil [pid] [interval] [count]。(这些工具的具体使用,也曾经总结过:http://brandnewuser.iteye.com/blog/2042528
 
2. 堆快照文件分析
 
可以通过jmap -dump:format=b,file=[filename] [pid] 来记录下堆的内存快照,利用第三方工具(mat,jhat)来分析整个Heap的对象关联情况。
 
如果内存耗尽可能导致JVM直接Crash掉,可以通过参数-XX:+HeapDumpOnOutOfMemoryError来配置当内存耗尽时记录下内存快照,可以通过-XX: HeapDumpPath来指定文件的路径,这个文件的命名格式如java_[pid].hprof。
 
3. JVM Crash日志分析
 
JVM有时也会因为一些原因而直接垮掉,因为JVM本身也是一个正在运行的程序,是程序就会有bug,也会导致JVM异常退出。JVM退出一般会在工作目录下产生一个日志文件,通过JVM参数来设定,如果-XX:ErrorFile=/tmp/log/hs_error_%p.log
 
JVM退出一般有三种主要的原因:
 
EXCEPTION_ACCESS_VIOLATION
 
正在运行JVM自己的代码,而不是外部的Java代码或其他类库代码,这种情况可能是JVM自己的bug,遇到这种错误可以根据出错信息oracle官网搜索一下发行的bug。大部分情况下是由于JVM的内存回收导致的,查看堆的内存占用情况。
 
SIGSEGV
 
JVM正在执行本地或JNI的代码,出这种错误很可能是第三方的本地库有问题,可以通过gbd和core文件来分析出错原因。
 
我们曾经遇到过一个这种类型的问题:http://brandnewuser.iteye.com/blog/2144456, 分析的过程比较复杂,但最终的原因却是无意中替换JNI包导致的。
 
EXCEPTION_STACK_OVERFLOW
 
这是个栈错误,注意JVM在执行Java线程出现的栈溢出通常不会导致JVM退出,而是抛出java.lang.StackOverflowError。但是在Java虚拟机中,Java的代码和本地C或C++代码共用相同的栈,这时如果出现栈溢出的话,就可能直接导致JVM退出,建议将JVM的栈尺寸调大,主要涉及两个参数: -Xss和-XX:StackShadowPages=n。
 
日志文件的Thread部分信息对排查问题的原因有很大的帮助,包括Machine Instructions和Thread Stack(虽然本人真的看不懂...)。
 
 
 
 
 
 
 
 

JVM内存管理和问题简要分析学习的更多相关文章

  1. JVM内存管理------垃圾搜集器参数精解

    本文是GC相关的最后一篇,这次LZ只是罗列一下hotspot JVM中垃圾搜集器相关的重点参数,以及各个参数的解释.废话不多说,这就开始. 垃圾搜集器文章传送门 JVM内存管理------JAVA语言 ...

  2. Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

    很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...

  3. JVM内存管理(二)

    JVM内存管理          JVM在执行java程序的过程中,会把内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖 ...

  4. JVM内存管理及垃圾回收

    一.JVM内存的构 Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法.总体分为下面几个部分: 程序计数器(Progra ...

  5. 现代JVM内存管理方法的发展历程,GC的实现及相关设计概述(转)

    JVM区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

  6. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  7. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  8. Java的内存 -JVM 内存管理

    一.综述 如果你学过C或者C++,那么你应该感受过它们对内存那种强大的掌控力.但是强大的能力往往需要更强大的控制力才能保证能力不被滥用,如果滥用C/C++的内存管理那么很容易出现指针满天飞的情况,不出 ...

  9. JVM内存管理及垃圾回收【转】

    很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...

随机推荐

  1. MVC4中视图获取控制器中返回的json格式数据

    再开发MVC项目时,有时只需要从控制器中返回一个处理的结果,这时返回Json格式的数据非常的方便,在Controller中,提供了几种返回类型和方法,如: Content() 返回文本类型的Conte ...

  2. 表单隐藏域与display:none

    有时候前端进行表单填写是分步骤的,每一步的时候其他步骤相关的表单视图不可见: 针对"不可见",以下有两种处理方式: ①display:none 这种方式呢,比较简单,就是将三个步骤 ...

  3. 使用nrm管理npm仓库

    使用nrm管理npm仓库 用npm装包的时候,经常碰到太慢或者npm官网被墙的情况,有时候凑合一下就改一下 "~/.npmrc" 文件,但是经常改来改去也挺麻烦的,于是找到了可以使 ...

  4. 将海康大华等网络摄像机RTSP流进行网页Flash rtmp和H5 hls直播的技术方案

    前言 再小的技术点也会有他的市场! 一直以来,都有一些不被看好,认为是成本太高,无法大规模展开的软件和产品形态,就好比每一座城市都会有他的著名小吃一样,即使是慕名而来的人源源不断,受众群体也总是有限, ...

  5. 我也说说Emacs吧(6) - Lisp速成

    前面我们学习了基本操作,也走马观花地看了不少emacs lisp的代码.这一章我们做一个lisp的速成讲座. Lisp的含义是表处理语言.它的代码组成结构都是用括号组成的表来表示的.Lisp中的功能, ...

  6. Bandit:一种简单而强大的在线学习算法

    假设我有5枚硬币,都是正反面不均匀的.我们玩一个游戏,每次你可以选择其中一枚硬币掷出,如果掷出正面,你将得到一百块奖励.掷硬币的次数有限(比如10000次),显然,如果要拿到最多的利益,你要做的就是尽 ...

  7. k近邻法( k-nearnest neighbor)

    基本思想: 给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分为这个类 距离度量: 特征空间中两个实例点的距离是两个实例点相似 ...

  8. C语言变量、函数的作用域及变量的存储方式

    一.变量的作用域和存储方式 在C语言中每个变量都有两种基本属性:数据类型.数据的存储类别. 数据类型很多人都已熟知,例如:字符型(char).整型(int).浮点型(float)等等.存储类别是指数据 ...

  9. scrollTop兼容处理

    使用jQuery2.0以下版本的scrollTop()函数来设置当然兼容性当然很好,但有时需要为滚动设置滑动效果.比如,使用animate函数,这里需要做些兼容性处理: 实例:http://sandb ...

  10. hive表数据导出到csv乱码原因及解决方案

    转载自http://blog.csdn.net/lgdlxc/article/details/42126225 Hive表中的数据使用hive - e"select * from table ...