`
footman265
  • 浏览: 118971 次
  • 性别: Icon_minigender_1
  • 来自: 宁波
社区版块
存档分类
最新评论

Java内存模型5

    博客分类:
  • j2SE
阅读更多

3.本机内存[部分内容来源于IBM开发中心]
  Java堆空间是在编写Java程序中被我们使用得最频繁的内存空间,平时开发过程,开发人员一定遇到过OutOfMemoryError,这种结果有可能来源于Java堆空间的内存泄漏,也可能是因为堆的大小不够而导致的,有时候这些错误是可以依靠开发人员修复的,但是随着Java程序需要处理越来越多的并发程序,可能有些错误就不是那么容易处理了。有些时候即使Java堆空间没有满也可能抛出错误,这种情况下需要了解的就是JRE(Java Runtime Environment)内部到底发生了什么。Java本身的运行宿主环境并不是操作系统,而Java虚拟机,Java虚拟机本身是用C编写的本机程序,自然它会调用到本机资源,最常见的就是针对本机内存的调用。本机内存是可以用于运行时进程的,它和Java应用程序使用的Java堆内存不一样,每一种虚拟化资源都必须存储在本机内存里面,包括虚拟机本身运行的数据,这样也意味着主机的硬件和操作系统在本机内存的限制将直接影响到Java应用程序的性能
  i.Java运行时如何使用本机内存:
  1)堆空间和垃圾回收
  Java运行时是一个操作系统进程(Windows下一般为java.exe),该环境提供的功能会受一些位置的用户代码驱动,这虽然提高了运行时在处理资源的灵活性,但是无法预测每种情况下运行时环境需要何种资源,这一点Java堆空间讲解中已经提到过了。在Java命令行可以使用-Xmx-Xms来控制堆空间初始配置,mx表示堆空间的最大大小,ms表示初始化大小,这也是上提到的启动Java的配置文件可以配置的内容。尽管逻辑内存堆可以根据堆上的对象数量和在GC上花费的时间增加或者减少,但是使用本机内存的大小是保持不变的,而且由-Xms的值指定,大部分GC算法都是依赖被分配的连续内存块的堆空间,因此不能在堆需要扩大的时候分配更多本机内存,所有的堆内存必须保留下来,请注意这里说的不是Java堆内存空间本机内存。
  本机内存保留本机内存分配不一样,本机内存被保留的时候,无法使用物理内存或者其他存储器作为备用内存,尽管保留地址空间块不会耗尽物理资源,但是会阻止内存用于其他用途,由保留从未使用过的内存导致的泄漏和泄漏分配的内存造成的问题其严重程度差不多,但使用的堆区域缩小时,一些垃圾回收器会回收堆空间的一部分内容,从而减少物理内存的使用。对于维护Java堆的内存管理系统,需要更多的本机内存来维护它的状态,进行垃圾收集的时候,必须分配数据结构来跟踪空闲存储空间和进度记录,这些数据结构的确切大小和性质因实现的不同而有所差异。
  2)JIT
  JIT编译器在运行时编译Java字节码来优化本机可执行代码,这样极大提高了Java运行时的速度,并且支持Java应用程序与本地代码相当的速度运行。字节码编译使用本机内存,而且JIT编译器的输入(字节码)和输出(可执行代码)也必须存储在本机内存里面,包含了多个经过JIT编译的方法的Java程序会比一些小型应用程序使用更多的本机内存。
  3)类和类加载器
  Java 应用程序由一些类组成,这些类定义对象结构和方法逻辑。Java 应用程序也使用 Java 运行时类库(比如 java.lang.String中的类,也可以使用第三方库。这些类需要存储在内存中以备使用。存储类的方式取决于具体实现。Sun JDK 使用永久生成(permanent generation,PermGen)堆区域,从最基本的层面来看,使用更多的类将需要使用更多内存。(这可能意味着您的本机内存使用量会增加,或者您必须明确地重新设置 PermGen 或共享类缓存等区域的大小,以装入所有类)。记住,不仅您的应用程序需要加载到内存中,框架、应用服务器、第三方库以及包含类的 Java 运行时也会按需加载并占用空间。Java 运行时可以卸载类来回收空间,但是只有在非常严酷的条件下才会这样做,不能卸载单个类,而是卸载类加载器,随其加载的所有类都会被卸载。只有在以下情况下才能卸载类加载器
  • Java 堆不包含对表示该类加载器的 java.lang.ClassLoader 对象的引用。
  • Java 堆不包含对表示类加载器加载的类的任何 java.lang.Class 对象的引用。
  • 在 Java 堆上,该类加载器加载的任何类的所有对象都不再存活(被引用)。

  需要注意的是,Java 运行时为所有 Java 应用程序创建的 3 个默认类加载器 bootstrapextension 和 application 都不可能满足这些条件,因此,任何系统类(比如 java.lang.String)或通过应用程序类加载器加载的任何应用程序类都不能在运行时释放。即使类加载器适合进行收集,运行时也只会将收集类加载器作为 GC 周期的一部分。一些实现只会在某些 GC 周期中卸载类加载器,也可能在运行时生成类,而不去释放它。许多 Java EE 应用程序使用 JavaServer Pages (JSP) 技术来生成 Web 页面。使用 JSP 会为执行的每个 .jsp 页面生成一个类,并且这些类会在加载它们的类加载器的整个生存期中一直存在 —— 这个生存期通常是 Web 应用程序的生存期。另一种生成类的常见方法是使用 Java 反射。反射的工作方式因 Java 实现的不同而不同,当使用 java.lang.reflect API 时,Java 运行时必须将一个反射对象(比如 java.lang.reflect.Field)的方法连接到被反射到的对象或类。这可以通过使用 Java 本机接口(Java Native Interface,JNI访问器来完成,这种方法需要的设置很少,但是速度缓慢,也可以在运行时为您想要反射到的每种对象类型动态构建一个类。后一种方法在设置上更慢,但运行速度更快,非常适合于经常反射到一个特定类的应用程序。Java 运行时在最初几次反射到一个类时使用 JNI 方法,但当使用了若干次 JNI 方法之后,访问器会膨胀为字节码访问器,这涉及到构建类并通过新的类加载器进行加载。执行多次反射可能导致创建了许多访问器类和类加载器,保持对反射对象的引用会导致这些类一直存活,并继续占用空间,因为创建字节码访问器非常缓慢,所以 Java 运行时可以缓存这些访问器以备以后使用,一些应用程序和框架还会缓存反射对象,这进一步增加了它们的本机内存占用。

  4)JNI
  JNI支持本机代码调用Java方法,反之亦然,Java运行时本身极大依赖于JNI代码来实现类库功能,比如文件和网络I/O,JNI应用程序可以通过三种方式增加Java运行时对本机内存的使用:
  • JNI应用程序的本机代码被编译到共享库中,或编译为加载到进程地址空间中的可执行文件,大型本机应用程序可能仅仅加载就会占用大量进程地址空间
  • 本机代码必须与Java运行时共享地址空间,任何本机代码分配本机代码执行内存映射都会耗用Java运行时内存
  • 某些JNI函数可能在它们的常规操作中使用本机内存,GetTypeArrayElementsGetTypeArrayRegion函数可以将Java堆复制到本机内存缓冲区中,提供给本地代码使用,是否复制数据依赖于运行时实现,通过这种方式访问大量Java堆数据就可能使用大量的本机内存堆空间
  5)NIO
  JDK 1.4开始添加了新的I/O类,引入了一种基于通道和缓冲区执行I/O的新方式,就像Java堆上的内存支持I/O缓冲区一样,NIO添加了对直接ByteBuffer的支持,ByteBuffer受本机内存而不是Java堆的支持,直接ByteBuffer可以直接传递到本机操作系统库函数,以执行I/O,这种情况虽然提高了Java程序在I/O的执行效率,但是会对本机内存进行直接的内存开销。ByteBuffer直接操作和非直接操作的区别如下:
  对于在何处存储直接 ByteBuffer 数据,很容易产生混淆。应用程序仍然在 Java 堆上使用一个对象来编排 I/O 操作,但持有该数据的缓冲区将保存在本机内存中,Java 堆对象仅包含对本机堆缓冲区的引用。非直接 ByteBuffer 将其数据保存在 Java 堆上的 byte[] 数组中。直接ByteBuffer对象会自动清理本机缓冲区,但这个过程只能作为Java堆GC的一部分执行,它不会自动影响施加在本机上的压力。GC仅在Java堆被填满,以至于无法为堆分配请求提供服务的时候,或者在Java应用程序中显示请求它发生。
  6)线程:
  应用程序中的每个线程都需要内存来存储器堆栈(用于在调用函数时持有局部变量并维护状态的内存区域)。每个 Java 线程都需要堆栈空间来运行。根据实现的不同,Java 线程可以分为本机线程和 Java 堆栈。除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)内部数据结构提供一些本机内存。尽管每个线程使用的内存量非常小,但对于拥有数百个线程的应用程序来说,线程堆栈的总内存使用量可能非常大。如果运行的应用程序的线程数量比可用于处理它们的处理器数量多,效率通常很低,并且可能导致糟糕的性能和更高的内存占用。
  ii.本机内存耗尽:
  Java运行时善于以不同的方式来处理Java堆空间的耗尽本机堆空间的耗尽,但是这两种情形具有类似症状,当Java堆空间耗尽的时候,Java应用程序很难正常运行,因为Java应用程序必须通过分配对象来完成工作,只要Java堆被填满,就会出现糟糕的GC性能,并且抛出OutOfMemoryError。相反,一旦 Java 运行时开始运行并且应用程序处于稳定状态,它可以在本机堆完全耗尽之后继续正常运行,不一定会发生奇怪的行为,因为需要分配本机内存的操作比需要分配 Java 堆的操作少得多。尽管需要本机内存的操作因 JVM 实现不同而异,但也有一些操作很常见:启动线程加载类以及执行某种类型的网络文件 I/O本机内存不足行为与 Java 堆内存不足行为也不太一样,因为无法对本机堆分配进行控制,尽管所有 Java 堆分配都在 Java 内存管理系统控制之下,但任何本机代码(无论其位于 JVM、Java 类库还是应用程序代码中)都可能执行本机内存分配,而且会失败。尝试进行分配的代码然后会处理这种情况,无论设计人员的意图是什么:它可能通过 JNI 接口抛出一个 OutOfMemoryError,在屏幕上输出一条消息,发生无提示失败并在稍后再试一次,或者执行其他操作。
  iii.例子:
  这篇文章一致都在讲概念,这里既然提到了ByteBuffer,先提供一个简单的例子演示该类的使用:
  ——[$]使用NIO读取txt文件——
package org.susan.java.io;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ExplicitChannelRead {
    public static void main(String args[]){
        FileInputStream fileInputStream;
        FileChannel fileChannel;
        long fileSize;
        ByteBuffer byteBuffer;
        try{
            fileInputStream = new FileInputStream("D:\\read.txt");
            fileChannel = fileInputStream.getChannel();
            fileSize = fileChannel.size();
            byteBuffer = ByteBuffer.allocate((int)fileSize);
            fileChannel.read(byteBuffer);
            byteBuffer.rewind();
            forint i = 0; i < fileSize; i++ )
                System.out.print((char)byteBuffer.get());
            fileChannel.close();
            fileInputStream.close();
        }catch(IOException ex){
            ex.printStackTrace();
        }
    }
}
  在读取文件的路径放上该txt文件里面写入:Hello World,上边这段代码就是使用NIO的方式读取文件系统上的文件,这段程序的输入就为:
Hello World
  ——[$]获取ByteBuffer上的字节转换为Byte数组——
package org.susan.java.io;

import java.nio.ByteBuffer;

public class ByteBufferToByteArray {
    public static void main(String args[]) throws Exception{
        // 从byte数组创建ByteBuffer
        byte[] bytes = new byte[10];
        ByteBuffer buffer = ByteBuffer.wrap(bytes);

        // 在position和limit,也就是ByteBuffer缓冲区的首尾之间读取字节
        bytes = new byte[buffer.remaining()];
        buffer.get(bytes, 0, bytes.length);

        // 读取所有ByteBuffer内的字节
        buffer.clear();
        bytes = new byte[buffer.capacity()];
        buffer.get(bytes, 0, bytes.length);
    }
}
  上边代码就是从ByteBuffer到byte数组转换过程,有了这个过程在开发过程中可能更加方便,ByteBuffer的详细讲解我保留到IO部分,这里仅仅是涉及到了一些,所以提供两段实例代码。
  
分享到:
评论

相关推荐

    java 内存模型 java 内存模型

    java 内存模型 java 内存模型 java 内存模型 java 内存模型

    Java 内存模型

    在Java 5之前,Java内存模型的描述比较模糊,为了提升多线程程序的可靠性,Java社区重新定义并强化了Java内存模型,该工作主要在JSR-133专家组的领导下完成。 JSR-133是Java社区的一个规范,它的全称是Java内存模型...

    深入理解Java内存模型

    Java内存模型是并发编程中一个至关重要的概念,它定义了共享变量的访问规则,以及这些变量如何在多线程环境下进行读写操作。在深入理解Java内存模型之前,我们需要先了解并发编程模型的分类,然后掌握Java内存模型的...

    cpu 内存模型和java内存模型

    Java程序员了解CPU以及相关的内存模型,对于深入理解Java内存模型以及并发编程至关重要。CPU作为计算机硬件的核心,其架构和工作原理影响着软件的性能和执行效率。尤其在Java这种多线程、高并发的编程语言中,对CPU...

    深入理解Java内存模型 pdf 超清版

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了程序中各个线程如何访问和修改共享变量,以及如何确保数据的一致性。深入理解Java内存模型对于编写高效的并发程序至关重要。本文...

    Java内存模型的历史变迁

    本文将探讨Java内存模型从早期版本到JDK 5的重大变革,并重点介绍这一变迁背后的动机及其对Java开发人员的实际意义。 #### 二、旧Java内存模型 在JDK 5之前,Java采用了一种基于共享内存的并发模型。在这种模型下...

    java内存模型文档

    这些文档如"Java内存模型.docx"、"Java内存模型2.docx"、"深入Java核心 Java内存分配原理精讲.docx"、"java内存模型.pdf"将深入探讨这些概念,帮助开发者更深入地理解Java内存模型及其在实际编程中的应用。...

    深度剖析java内存模型

    Java内存模型(Java Memory Model,简称JMM)是Java虚拟机(JVM)规范中定义的一种内存模型,它涉及了线程之间共享变量的可见性问题。在并发编程中,理解Java内存模型对于编写正确的多线程程序至关重要。 首先,...

    深入理解 Java 内存模型_程晓明_InfoQ_java_内存模型_

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了线程如何共享和访问内存,以及在多线程环境中如何保证数据一致性。理解JMM对于编写高效、正确且线程安全的Java代码至关重要。 ...

    Java内存模型详解JMM.docx

    Java内存模型详解JMM Java内存模型(Java Memory Model,JMM)是Java虚拟机(JVM)中的一种内存模型,它描述了程序中各个变量之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节...

    Java内存模型详解

    ### Java内存模型详解 #### 1. JMM简介 ##### i. 内存模型概述 Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)的一部分,用于规定程序中的各种变量(包括实例字段、静态字段和数组元素等)在多个...

    深入理解java内存模型

    Java内存模型(Java Memory Model,JMM)是Java平台中非常关键的概念,它定义了线程如何共享和访问内存中的数据,以及在多线程环境下如何保证数据的一致性。这本书"深入理解Java内存模型"显然是为了帮助读者深入探讨...

    java内存模型.pdf

    Java内存模型(JMM)是Java虚拟机(JVM)的一部分,它定义了程序中不同变量如何交互,特别是在多线程环境下。JMM确保了在各种硬件和操作系统平台上,Java程序的行为具有一致性和可预测性。Java内存模型的主要目标是...

    深入理解 Java 内存模型

    Java 内存模型(Java Memory Model,简称 JMM)是 Java 平台中关于线程如何访问共享变量的一套规则,它定义了线程之间的内存可见性、数据一致性以及指令重排序等关键概念,对于多线程编程和并发性能优化至关重要。...

    java内存模型详解--非常经典

    Java内存模型(JVM Memory Model,简称JMM)是Java平台中的一个重要概念,它定义了程序中各个变量的访问规则,以及在多线程环境下的内存一致性效果。JMM主要解决的是并发环境下不同线程之间如何共享数据以及如何保证...

    《深入理解JAVA内存模型》PDF

    Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main ...

    java内存模型详解

    Java内存模型,简称JMM(Java Memory Model),是Java虚拟机规范中定义的一个抽象概念,它规定了程序中各个线程如何访问共享变量,以及对这些访问进行同步控制的规则。理解Java内存模型对于编写多线程并发程序至关...

    JAVA内存模型与垃圾回收

    JAVA内存模型与垃圾回收是Java开发中至关重要的概念,它们直接影响到程序的性能和稳定性。首先,我们来看看Java内存模型。 Java内存模型,通常被称为JVM内存模型,它定义了程序中不同部分如何访问和共享数据。在...

Global site tag (gtag.js) - Google Analytics