转自:http://spaces.msn.com/songsun/
以下继续探讨,说说jvm的线程(thread)及其执行中和内存相关的问题。but今天是写不完了,请保持关注,先写个预告栏咯
记得当年学java之初,总是很鄙夷它,因为那时候对c/c++很痴迷,结果呢,第一个多线程程序还是拿java写出来的。而线程(thread),本是
操作系统的所提供/支持的,所以当初有一段时间,我总在怀疑我那java程序创建的线程到底是不是真正的操作系统线程,现在看来很可笑。
题外,再继续质疑一下:
在有些平台上,比如Linux或者大
部分的unix,只有进程而没有线程的概念的,但这些平台上,进程间通讯的手段极其发达,所以jvm会用进程模拟线程,效果也是一样的。不过,也有一些平
台如sorlaris,在java线程和os内核线程间,存在着不是1对1的关系,实在让我挠头,容以后再去把它搞明白。
简单的说,jvm如何实现多线程呢,首先要有os的支持包括变相的支持,那么jvm通过os提供线程接口创建新线程,这些新线程当然还不能直接执行你的
thread对象的run方法,但它会拥有并执行属于该线程的一个java
interpreter实例或者上下文,java解释器再去执行你的run方法,当run方法结束(自然结束时),java
interpreter发现没有字节码可以解释了,也宣告结束,OS线程也就自然终结了。至于要实现线程的终止,挂起,恢复,也都可以从OS那里找到相应
的接口,这里不再细说,而主要关注对内存的影响,主要是java stack和native call等问题。
待续
续
当执行引擎(即java
interpreter,对应一个ExecEnv)开始解释执行一个java method以前,它首先初始化自身,即分配自己的initial
java stack,栈开始时尺寸很小,随着使用的需要而扩展,存在一个400k的默认长度限制(
-Xss<size>可更改该值);ExecEnv有两个重要的标志(或指针),一个是pc,即程序计数器,另一个是optop,即栈顶,也
是最上面一个操作数(即本地变量)的位置。
pc总是指向要执行的byte code(随着每一条byte
code的执行,pc自动改变),而optop指向java stack的顶端(也随某些byte
code的执行而改动)。当方法执行时,方法的invoker将pc指向method block的code位,同时,在java
stack里为该方法新建java frame(每一个方法在编译期就确定了运行它所需的最大stack size,所以,新建java
freame时,会按照这个size去检查java stack是否满足,若不足,则扩展java stack,而java
stack已经达到最大长度限制时,扩展失败,发生stack
overflow),准备工作完成后,还给ExecEnv继续解释和执行;当方法执行完毕时,一条ret字节指令将pc和optop送回前一个java frame所记录的位置。所以,对于某个线程来说,其java
stack完整的记录了所深入的每一层java method(每层一个java frame),除了native method,本质上是因为c的堆栈不能记录。
以上是执行java method,若是native method呢?很简单,native
method在类加载时,即被jvm安排了一个native invoker在其method
block中,继而在ExecEnv执行一条call该native method的字节指令时,native
invoker被执行,如果是首次执行,那么这个native mothod的method block的code段尚未挂接,native
invoker检查到这个信息,将根据method
name和signature搜索系统中已加载的dll的合适挂接点,将找到的挂接点挂在code上,而后调用该code段即可(找不到挂接点,会发生什
么异常你应该清楚)。过程中,除非你的native method中又反调了java method,那么,java stack将毫无变化。
再来关注一下native
method的内存模型:native
mthod自身的机器码code存在于dll镜像(image)中;nm的本地变量存在于线程堆栈内(指native thread,和java
thread是绑定着的);nm用到的静态变量存在于dll的共享变量区中;nm制造的java对象(通过jni手段new来的)依然在java
heap;而nm声明的global ref以及local ref存放在jvm的native c
heap上。本地方法也会带来内存泄露(c的内存泄露,也可能是java heap的global ref的未释放),特别是c
heap的泄露,运行期是极难探测和定位的。
最后,说说调用深度过深(即java stack会很长)带来的负面影响。在前一节,我们知道本地变量都寄存在java
stack中。如果调用深度过深,特别是jvm长时间运行在较高深度调用的情形下,意味着在栈上的各frame中,存在着很多变量,它们中的引用类型将一
直保持对java heap上对应对象的引用而导致gc器始终不能释放它们;其次,较长的java
stack也给gc器造成了较多的扫描时间(为何要扫描后面再说);第三,method call本身也是一种时间开销,对于很短小的method,call它比它本身代码执行的时间还要多,好比机关枪打蚊子,这也是现代编译器非常讲究inline优化的原因。
分享到:
相关推荐
3. Thread Dump:使用jstack命令获取线程快照,分析阻塞或死锁情况。 4. 监控GC日志:通过-XX:+PrintGC、-XX:+PrintGCDetails、-XX:+PrintGCDateStamps等参数,记录并分析垃圾收集行为。 5. 分析内存泄漏:利用内存...
jmap - JVM Memory Map Tool **用途**:生成虚拟机的内存映像文件,用于分析内存泄露等问题。 **命令格式**: ``` jmap [option] pid ``` **执行示例**: ``` jmap -dump:format=b,file=/tmp/dump3700.hprof ...
### Sun JVM原理与内存管理 #### 一、Sun JDK 1.6 GC (Garbage Collector) Sun JDK 1.6 的垃圾收集器(GC)是其内存管理的关键组成部分,它负责自动地回收不再使用的对象所占用的内存。本文将详细介绍Sun JDK 1.6 GC...
3. **jinfo(Configuration Info for Java)**:用于查看和修改JVM配置信息,如JVM选项、系统属性等,对于排查特定环境下的问题很有帮助。 4. **jhat(Java Heap Analysis Tool)**:当JVM生成堆转储文件后,jhat...
3. **Garbage Collection(GC)问题**:JVM的垃圾收集机制可能导致性能瓶颈。通过监控GC日志,我们可以分析GC行为,如暂停时间、内存分配情况等。 4. **类装载问题**:类装载器错误可能引发NoClassDefFoundError或...
- **直接内存(Direct Memory)**:不是JVM运行时数据区的一部分,但在某些场景下会被频繁使用,比如通过ByteBuffer的allocateDirect方法分配的内存。 #### 堆内存划分与回收 堆内存通常被划分为几个部分,包括...
3. **程序计数器**:这是一个非常小的内存区域,用于存储当前线程正在执行的字节码指令的地址。由于Java是多线程的,每个线程都有自己的程序计数器,这样可以确保线程在被调度时能准确地找到下一条要执行的指令。 4...
- **Thread Dump**:线程快照,记录当前所有线程的状态及调用栈信息。 - **Heap Dump**:堆快照,记录了堆内存中所有的对象及其相关信息。 #### 三、常用命令及应用场景 ##### 1. **top** - **命令格式**:`top` ...
3. **线程栈(Thread Stack)**:用于存储线程的局部变量、栈帧等。 #### 堆内存配置详解 堆内存是JVM管理的主要区域之一,也是最常发生内存溢出的地方。合理的堆内存配置可以显著提升应用性能。以下是一些重要的...
3. **垃圾回收**:JVM负责自动管理内存,当对象不再被引用时,垃圾回收器会回收其占用的空间。常见的垃圾收集算法有标记-清除、复制、标记-整理和分代收集等。新生代、老年代和持久代是根据对象生命周期进行划分的...
介绍了heap dump和thread dump,以及详细介绍dump工具Memory Analyzer的使用,最后讲解了Java对象的内存布局。
heap dump是Java虚拟机(JVM)在某个时间点对堆内存快照的记录,包含了所有对象及其引用关系。当应用程序出现性能问题或者内存耗尽时,生成heap dump文件至关重要。MAT可以打开并解析这些文件,为用户提供深入的内存...
- **Java内存模型(JMM, Java Memory Model)**:定义了线程之间的共享变量如何在内存中交互和同步的规则。 - **工作内存与主内存**:每个线程都有自己的工作内存,包含对共享变量的副本。线程间通过主内存交换信息...
《JVM调优总结》与《Java虚拟机:JVM高级特性与最佳实践》是两本深入探讨Java虚拟机(JVM)的书籍,对于Java开发者来说,它们提供了丰富的知识和实践经验,尤其对于想要理解JVM工作原理以及进行性能优化的专业人士更...
二、jstack (Java Thread Stack Trace) jstack用于查看JVM的线程堆栈信息,帮助诊断线程阻塞或死锁问题。基本语法: ``` jstack [option] pid ``` 选项通常包括: - `-l`:详细输出,包括锁信息。 例如,`jstack -l...
--total-memory :应用程序可用的总内存,通常用大小分类( B 、 K 、 M 、 G 、 T )表示 --loaded-class-count :应用程序运行时将加载的类数 --thread-count : 用户线程数 --jvm-options : JVM 选项,通常是JAVA...
"JVM堆内存分析工具"如HA(HeapAnalyzer)和MAT(Memory Analyzer Tool)就是专门为此设计的,它们能够帮助开发者深入洞察内存的分配、使用以及可能存在的内存泄漏问题。 首先,HA(HeapAnalyzer)通常是一个简单的...
Thread Dump,即线程快照,是指Java虚拟机(JVM)在某一时间点捕捉到的所有线程的状态快照。通过分析Thread Dump,我们可以了解到每个线程当时的运行情况,包括线程的运行状态、调用栈等关键信息,这对于理解系统内部...
- 直接内存(Direct Memory):使用Native方法直接分配堆外内存。 4. **垃圾收集** - 垃圾收集的基本概念与目标。 - 垃圾收集器类型:串行、并行、并发、G1、ZGC、Shenandoah等。 - 分代收集理论:年轻代、老...
主要包括主内存(Shared Memory)和每个线程的工作内存(Thread Local Memory),线程间通信必须通过主内存交换。 8. **垃圾收集与对象存活判定**: - 使用可达性分析算法,通过GC Roots来判断对象是否可达。如果...