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. java.util.Collection List与其子类 Set与其子类

    package com.Collection; import java.util.ArrayList; import java.util.Collection; import java.util.It ...

  2. Cassandra cqlsh - connection refused

    启动cqlsh时,保存如下: Connection error: ('Unable to connect to any servers', {'127.0.0.1': error(111, " ...

  3. java语言基础-变量

    一丶变量的基本概念 1.什么是变量 (1).内存中的一个存储区域 (2).该区域有自己的名称(变量名),和类型(数据类型) (3.)该区域的数据可以在同一类型范围内不断变化(定义变量的主要目的是因为数 ...

  4. C++11_ Variadic Templates

    版权声明:本文为博主原创文章,未经博主允许不得转载. 这次主要介绍C++11的又一个新特性 Variadic Templates (可变模板参数) 它的实现类似于initializer_list< ...

  5. vim+ctags用法

    vim用法     在VIM编辑器的环境下用":make"就可以编译程序,如果程序中有错误,就会显示出来.          下列命令可以快速定位,并修改错误错误 ":c ...

  6. BitArray编写埃拉托斯特尼筛法(原书错误,学习更正)

    刚开始代码无法运行,修改后原书代码可以运行了,可是书本的思想还是错的. 虽然接下来的都是讲错误的思想下的“错误”的修改. 原书缺了窗体控件的代码,虽然在VS下不需要手动写窗体的代码,但是刚开始确实也不 ...

  7. iOS开发-UITextView文字排版

    UITextView文本排版 1.配置NSMutableParagraphStyle NSMutableParagraphStyle *MParaStyle = [[NSMutableParagrap ...

  8. iOS-分组UITableView删除崩溃问题(当删除section中最后一条数据崩溃的情况)

    错误: The number of sections contained in the table view after the update (1) must be equal to the num ...

  9. redis整合异常总结

    问题:org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field ' ...

  10. test20190308

    测试 晚上考试,是 \(SCOI\ 2016\ Day\ 2\) 的题目. 妖怪 由于之前在洛谷上用三分水过去了,就很 \(naive\) 地打了一个三分就跑了.获得 \(10\) 分好成绩. 记 \ ...