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(虽然本人真的看不懂...)。
相关推荐
在这份由Sun Microsystems公司出版的《JVM内存管理白皮书》中,我们可以找到关于Java虚拟机(JVM)内存管理的详细介绍和深入分析。这份文档对于想要深入了解JVM工作原理的读者来说是一份宝贵的学习资料。在这份...
对于Java内存模型和垃圾回收机制,笔记可能会介绍JVM的工作原理,包括堆内存、栈内存、方法区以及新生代、老年代的划分,还有如何优化内存使用和避免内存泄漏。 此外,数据库相关知识也是面试中的常见考点,包括SQL...
Java作为一门广泛使用的编程语言,其核心知识点十分丰富。从虚拟机到集合框架,再到各种设计模式和算法,Java的学习涉及了...通过掌握这些内容,可以更好地理解Java程序的运行机制,以及如何高效地进行开发和问题解决。
JVM在幕后处理内存管理和垃圾收集,减轻了程序员的负担。然而,这同时也意味着开发者需要对JVM的工作原理有一定的了解,以便在必要时进行性能调优。本章节简要介绍JVM调优的重要性及基本概念。 **2. JAVA虚拟机运行...
4. **JVM内存模型**:了解JVM如何管理内存对优化性能至关重要。这包括堆内存、栈内存、方法区、本地方法栈以及垃圾回收机制。 5. **异常处理**:Java中的异常处理是程序健壮性的重要组成部分,包括try-catch-...
在IT行业中,Web服务器性能调优是至关重要的,它直接影响着网站的响应速度、稳定性和用户体验。...通过深入学习和实践这些内容,可以有效地提升Java Web应用的运行效率,满足高并发、大数据量的业务需求。
10. **JVM与编译器**:简要介绍Java虚拟机的工作方式和优化技巧,以及如何分析和调试代码。 11. **设计模式**:介绍了一些常见的设计模式,如单例模式、工厂模式、观察者模式等,这些模式在实际编程中有着广泛的...
以上是对JBoss教程的简要概述,深入学习还需参考《JBoss教程》PDF文档,其中详细介绍了每个知识点的实践操作和示例,是学习和掌握JBoss不可或缺的资料。通过本教程的学习,你将能够熟练地在JBoss上开发、部署和管理...
在Java编程中,类装入器是Java虚拟机(JVM)的关键组成部分,负责将类文件从磁盘加载到内存中并执行。Eclipse有自己的类装入机制,它与标准JVM有所不同,能够支持插件体系结构。Eclipse类装入器允许开发者自定义类...
通过深入研究这些源码,开发者可以了解Spark内部的工作原理,包括RDD(弹性分布式数据集)、DAG(有向无环图)调度、内存管理、容错机制等核心概念。此外,还能学习如何使用Spark SQL进行数据查询,利用MLlib构建...
同时,讨论了Java中的线程模型,如Java虚拟机(JVM)如何调度和管理线程。 2. **同步机制**:详细讲解了Java中的同步工具,如`synchronized`关键字、`volatile`变量、`java.util.concurrent`包中的各种并发工具类...
- 自动内存管理:Java的垃圾回收机制自动处理内存分配和释放,降低了程序员的工作负担。 1.3 Python Python是一种高级编程语言,以其简洁明了的语法和丰富的库资源受到开发者喜爱。Python的特点包括: - 易读性:...
8. **Java虚拟机(JVM)**:探讨了JVM的工作原理,包括内存管理、垃圾收集以及性能优化,有助于开发者写出更高效的代码。 9. **泛型**:详细解读了Java泛型的使用,包括类型擦除、通配符和边界,提高了代码的类型...
15. **进阶主题** - 涵盖一些高级主题,如并发编程、内存管理和性能优化,以及设计模式的使用。 通过阅读这本电子书,读者将能够全面理解JAVA语言,并具备开发复杂应用程序的能力。无论是准备面试,还是进行实际...
7. **JVM原理**:基础的Java虚拟机工作原理,内存管理(堆、栈、方法区)及垃圾回收机制。 8. **HTML**:学习基本的HTML元素,布局,表单,链接,图像等,构建静态网页。 9. **CSS**:掌握选择器,盒模型,布局...
2. **内存管理** - 垃圾回收机制(GC)的工作原理,了解新生代、老年代、永久代的划分。 - 分代垃圾回收器,如Serial、ParNew、CMS、G1等的特性与应用场景。 - 深拷贝与浅拷贝的区别,以及如何实现对象的深拷贝和...
- Java基础增强: 包括并发编程、JVM内存管理等。 - Redis: 一种高性能的键值存储数据库。 #### 总结 通过以上内容的学习,我们可以了解到DOS命令的基本使用方法,以及Linux系统的基础知识和常用命令。此外,对于...
6. **JDB高级特性**:探索JDB的高级功能,如线程管理、内存检查、异常处理等。 7. **命令行调试优势**:了解相对于图形化调试器,命令行调试的灵活性和效率提升。 8. **实战演练**:通过实际操作示例代码,熟悉JDB...