导航:首页 > 方法技巧 > jvm知识导图如何调用方法的

jvm知识导图如何调用方法的

发布时间:2022-11-06 18:04:18

① Java中如何调用函数和自定义函数

调用函数

System.out.println("hello world");

println即为函数,out为是输出对象,System为java系统类。

Java源程序(.java文件)——>java字节码文件(.class文件)——>由解释执行器(java.exe)将字节码文件加载到java虚拟机(jvm)——>字节码文件(.class)就会在java虚拟机中执行。

Java的基本包
java.lang其中包含有:
接口:Comparable、Cloneable、Runable等
类:八个基本数据类型封装类、Math、Runtime、Object、String、StringBuffer、Thread、Exception等

② Spark性能调优篇七之JVM相关参数调整

        由于Spark程序是运行在JVM基础之上的,所以我们这一篇来讨论一下关于JVM的一些优化操作。在开始JVM调优操作之前,我们先通过一张图看一下JVM简单的内存划分情况。

        关于JVM内存的深入知识在这里不赘述,请大家自行对相关知识进行补充。好,说回Spark,运行Spark作业的时候,JVM对会对Spark作业产生什么影响呢?答案很简单,如果数据量过大,一定会导致JVM内存不足。在Spark作业运行时,会创建出来大量的对象,每一次将对象放入JVM时,首先将创建的对象都放入到eden区域和其中一个survivor区域中;当eden区域和一个survivor区域放满了以后,这个时候会触发minor gc,把不再使用的对象全部清除,而剩余的对象放入另外一个servivor区域中。JVM中默认的eden,survivor1,survivor2的内存占比为8:1:1。当存活的对象在一个servivor中放不下的时候,就会将这些对象移动到老年代。如果JVM的内存不够大的话,就会频繁的触发minor gc,这样会导致一些短生命周期的对象进入到老年代,老年代的对象不断的囤积,最终触发full gc。一次full gc会使得所有其他程序暂停很长时间。最终严重影响我们的Spark的性能和运行速度。

基于以上原因, 我们的第一个JVM优化点就是降低cache操作的内存占比 ;

        spark中,堆内存又被划分成了两块儿,一块儿是专门用来给RDD的cache、persist操作进行RDD数据缓存用的;另外一块儿,就是我们刚才所说的,用来给spark算子函数的运行使用的,存放函数中自己创建的对象。默认情况下,给RDD cache操作的内存占比是0.6,即60%的内存都给了cache操作了。但是问题是,如果某些情况下cache占用的内存并不需要占用那么大,这个时候可以将其内存占比适当降低。怎么判断在什么时候调整RDD cache的内存占用比呢?其实通过Spark监控平台就可以看到Spark作业的运行情况了,如果发现task频繁的gc,就可以去调整cache的内存占用比了。通过SparkConf.set("spark.storage.memoryFraction","0.6")来设定。

    我们第二个JVM优化点是 堆外内存和连接等待时长的调整 ;其实这两个参数主要是为了解决一些Spark作业运行时候出现的一些错误信息而进行调整的。下面我们来分别介绍一下这两个点。

1.堆外内存的调整

a) 问题提出

        有时候,如果你的spark作业处理的数据量特别特别大,几亿数据量;然后spark作业一运行就会出现类似shuffle file cannot find,executor、task lost,out of memory(内存溢出)等这样的错误。这是因为可能是说executor的堆外内存不太够用,导致executor在运行的过程中,可能会内存溢出;然后可能导致后续的stage的task在运行的时候,可能要从一些executor中去拉取shuffle map output文件,但是executor可能已经挂掉了,关联的blockmanager也没有了;所以可能会报shuffle  output file not found;resubmitting task;executor lost 这样的错误;最终导致spark作业彻底崩溃。

        上述情况下,就可以去考虑调节一下executor的堆外内存。也许就可以避免报错;此外,有时,堆外内存调节的比较大的时候,对于性能来说,也会带来一定的提升。

b) 解决方案:

--conf  spark.yarn.executor.memoryOverhead=2048

        在spark-submit脚本里面添加如上配置。默认情况下,这个堆外内存上限大概是300多M;我们通常项目中真正处理大数据的时候,这里都会出现问题导致spark作业反复崩溃无法运行;此时就会去调节这个参数,到至少1G或者更大的内存。通常这个参数调节上去以后,就会避免掉某些OOM的异常问题,同时呢,会让整体spark作业的性能,得到较大的提升。

2.连接等待时长的调整

a) 问题提出:

        由于JVM内存过小,导致频繁的Minor gc,有时候更会触犯full gc,一旦出发full gc;此时所有程序暂停,导致无法建立网络连接;spark默认的网络连接的超时时长是60s;如果卡住60s都无法建立连接的话,那么就宣告失败了。碰到一种情况,有时候报错信息会出现一串类似file id not found,file lost的错误。这种情况下,很有可能是task需要处理的那份数据的executor在正在进行gc。所以拉取数据的时候,建立不了连接。然后超过默认60s以后,直接宣告失败。几次都拉取不到数据的话,可能会导致spark作业的崩溃。也可能会导致DAGScheler,反复提交几次stage。TaskScheler,反复提交几次task。大大延长我们的spark作业的运行时间。

b) 解决方案:

--conf spark.core.connection.ack.wait.timeout=300

        在spark-submit脚本中添加如上参数,调节这个值比较大以后,通常来说,可以避免部分的偶尔出现的某某文件拉取失败,某某文件lost掉的错误。

总结:本文关于JVM的相关调优暂时先到这里,关于Spark中的JVM相关知识后面会在trouble shutting的时候还会继续介绍。后续还会不断更新关于Spark作业优化的一些其他方式,欢迎关注。

z小赵    Spark性能调优之JVM相关参数调整

③ java中图形界面ListModel的用法方法如何调用

AbstractListModel这个类的功能是抽象定义一个带内容的list,你程序 MyListModel类中继承并实现了这个抽象类,this.list1=new JList(new MyListModel());这几句话中的new MyListMode()不就是实例化一个对象,这个对象就是一个带内容的list,就是用这个对象的内容实例化一个JList。
希望我的回答对你有帮助,并及时采纳

④ java思维导图

Java虚拟机是Java语言的运行环境,它是Java别具吸引力的特性之一,属于Java的中级内容。在学习过Java初级知识后,工程师就需要学习Java虚拟机。

周志明的《深入理解Java虚拟机》详细的介绍了Java虚拟机,但是学习的过程中会发现书本很厚,知识点很多,我最开始是采用有道云笔记去记笔记,但是发现知识点过于分散,朋友建议我绘制Java虚拟机的思维导图,更有助于学习Java虚拟机。

⑤ 请描述一下JVM加载class文件的原理机制

原理:Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。

在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

类装载方式,有两种 :

1、隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。

2、显式装载, 通过class.forname()等方法,显式加载需要的类

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

(5)jvm知识导图如何调用方法的扩展阅读:

Java的类加载器有三个,对应Java的三种类:

Bootstrap Loader :启动类加载器,是虚拟机自身的一部分。负责将存放在lib目录中的类库加载到虚拟机中。其无法被Java程序直接引用。负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)。

ExtClassLoader :负责加载扩展类(就是继承类和实现类)。

AppClassLoader :负责加载用户类路径(ClassPath)上所指定的类库(程序员自定义的类)。

JVM中类的加载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。

当类被加载后就进入连接阶段,这一阶段包括

验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

⑥ 理解JVM怎么使用Windows和Linux下的本机内存

Java™ 堆耗尽并不是造成 java.lang.OutOfMemoryError 的惟一原因。如果本机内存 耗尽,则会发生普通调试技巧无法解决的OutOfMemoryError。本文将讨论本机内存的概念,Java 运行时如何使用它,它被耗尽时会出现什么情况,以及如何在 Windows® 和 Linux® 上调试本机 OutOfMemoryError。针对 AIX® 系统的相同主题将在 另一篇同类文章 中介绍。
Java 堆(每个 Java 对象在其中分配)是您在编写 Java 应用程序时使用最频繁的内存区域。JVM 设计用于将我们与主机的特性隔离,所以将内存当作堆来考虑再正常不过了。您一定遇到过 Java 堆 OutOfMemoryError ,它可能是由于对象泄漏造成的,也可能是因为堆的大小不足以存储所有数据,您也可能了解这些场景的一些调试技巧。但是随着您的 Java 应用程序处理越来越多的数据和越来越多的并发负载,您可能就会遇到无法使用常规技巧进行修复的 OutOfMemoryError。在一些场景中,即使 java 堆未满,也会抛出错误。当这类场景发生时,您需要理解 Java 运行时环境(Java Runtime Environment,JRE)内部到底发生了什么。
Java 应用程序在 Java 运行时的虚拟化环境中运行,但是运行时本身是使用 C 之类的语言编写的本机程序,它也会耗用本机资源,包括本机内存。 本机内存是可用于运行时进程的内存,它与 Java 应用程序使用的 java 堆内存不同。每种虚拟化资源(包括 Java 堆和 Java 线程)都必须存储在本机内存中,虚拟机在运行时使用的数据也是如此。这意味着主机的硬件和操作系统施加在本机内存上的限制会影响到 Java 应用程序的性能。
本系列文章共分两篇,讨论不同平台上的相应话题。本文是其中一篇。在这两篇文章中,您将了解什么是本机内存,Java 运行时如何使用它,本机内存耗尽之后会发生什么情况,以及如何调试本机 OutOfMemoryError。本文介绍 Windows 和 Linux 平台上的这一主题,不会介绍任何特定的运行时实现。另一篇 类似的文章 介绍 AIX 上的这一主题,着重介绍 IBM® Developer Kit for Java。(另一篇文章中关于 IBM 实现的信息也适合于除 AIX 之外的平台,因此如果您在 Linux 上使用 IBM Developer Kit for Java,或使用 IBM 32-bit Runtime Environment for Windows,您会发现这篇文章也有用处)。
本机内存简介
我将首先解释一下操作系统和底层硬件给本机内存带来的限制。如果您熟悉使用 C 等语言管理动态内存,那么您可以直接跳到 下一节。
硬件限制
本机进程遇到的许多限制都是由硬件造成的,而与操作系统没有关系。每台计算机都有一个处理器和一些随机存取存储器(RAM),后者也称为物理内存。处理器将数据流解释为要执行的指令,它拥有一个或多个处理单元,用于执行整数和浮点运算以及更高级的计算。处理器具有许多寄存器 —— 常快速的内存元素,用作被执行的计算的工作存储,寄存器大小决定了一次计算可使用的最大数值。
处 理器通过内存总线连接到物理内存。物理地址(处理器用于索引物理 RAM 的地址)的大小限制了可以寻址的内存。例如,一个 16 位物理地址可以寻址 0x0000 到 0xFFFF 的内存地址,这个地址范围包括 2^16 = 65536 个惟一的内存位置。如果每个地址引用一个存储字节,那么一个 16 位物理地址将允许处理器寻址 64KB 内存。
处理器被描述 为特定数量的数据位。这通常指的是寄存器大小,但是也存在例外,比如 32 位 390 指的是物理地址大小。对于桌面和服务器平台,这个数字为 31、32 或 64;对于嵌入式设备和微处理器,这个数字可能小至 4。物理地址大小可以与寄存器带宽一样大,也可以比它大或小。如果在适当的操作系统上运行,大部分 64 位处理器可以运行 32 位程序。
表 1 列出了一些流行的 Linux 和 Windows 架构,以及它们的寄存器和物理地址大小:

表 1. 一些流行处理器架构的寄存器和物理地址大小

架构
寄存器带宽(位)
物理地址大小(位)

(现代)Intel® x86
32 32
36,具有物理地址扩展(Pentium Pro 和更高型号)

x86 64
64 目前为 48 位(以后将会增大)

PPC64
64 在 POWER 5 上为 50 位

390 31 位
32 31

390 64 位
64 64

操作系统和虚拟内存
如果您编写无需操作系统,直接在处理器上运行的应用程序,您可以使用处理器可以寻址的所有内存(假设连接到了足够的物理 RAM)。但是要使用多任务和硬件抽象等特性,几乎所有人都会使用某种类型的操作系统来运行他们的程序。
在 Windows 和 Linux 等多任务操作系统中,有多个程序在使用系统资源。需要为每个程序分配物理内存区域来在其中运行。可以设计这样一个操作系统:每个程序直接使用物理内存,并 且可以可靠地仅使用分配给它的内存。一些嵌入式操作系统以这种方式工作,但是这在包含多个未经过集中测试的应用程序的环境中是不切实际的,因为任何程序都 可能破坏其他程序或者操作系统本身的内存。
虚拟内存 允许多个进程共享物理内存,而且不会破坏彼此的数据。在具有虚拟内存的操作系统(比如 Windows、Linux 和许多其他操作系统)中,每个程序都拥有自己的虚拟地址空间 —— 一个逻辑地址区域,其大小由该系统上的地址大小规定(所以,桌面和服务器平台的虚拟地址空间为 31、32 或 64 位)。进程的虚拟地址空间中的区域可被映射到物理内存、文件或任何其他可寻址存储。当数据未使用时,操作系统可以在物理内存与一个交换区域 (Windows 上的页面文件 或者 Linux 上的交换分区)之间移动它,以实现对物理内存的最佳利用率。当一个程序 尝试使用虚拟地址访问内存时,操作系统连同片上硬件会将该虚拟地址映射到物理位置,这个位置可以是物理 RAM、一个文件或页面文件/交换分区。如果一个内存区域被移动到交换空间,那么它将在被使用之前加载回物理内存中。图 1 展示了虚拟内存如何将进程地址空间区域映射到共享资源:

程序的每个实例以进程 的形式运行。在 Linux 和 Windows 上,进程是一个由受操作系统控制的资源(比如文件和套接字信息)、一个典型的虚拟地址空间(在某些架构上不止一个)和至少一个执行线程构成的集合。
虚 拟地址空间大小可能比处理器的物理地址大小更小。32 位 Intel x86 最初拥有的 32 位物理地址仅允许处理器寻址 4GB 存储空间。后来,添加了一种称为物理地址扩展(Physical Address Extension,PAE)的特性,将物理地址大小扩大到了 36 位,允许安装或寻址至多 64GB RAM。PAE 允许操作系统将 32 位的 4GB 虚拟地址空间映射到一个较大的物理地址范围,但是它不允许每个进程拥有 64GB 虚拟地址空间。这意味着如果您将大于 4GB 的内存放入 32 位 Intel 服务器中,您将无法将所有内存直接映射到一个单一进程中。
地址窗口扩展(Address Windowing Extension)特性允许 Windows 进程将其 32 位地址空间的一部分作为滑动窗口映射到较大的内存区域中。Linux 使用类似的技术将内存区域映射到虚拟地址空间中。这意味着尽管您无法直接引用大于 4GB 的内存,但您仍然可以使用较大的内存区域。
内核空间和用户空间
尽管每个进程都有其自己的地址空间,但程序通常无法使用所有这些空间。地址空间被划分为用户空间 和内核空间。内核是主要的操作系统程序,包含用于连接计算机硬件、调度程序以及提供联网和虚拟内存等服务的逻辑。
作为计算机启动序列的一部分,操作系统内核运行并初始化硬件。一旦内核配置了硬件及其自己的内部状态,第一个用户空间进程就会启动。如果用户程序需要来自操作系统的服务,它可以执行一种称为系统调用 的操作与内核程序交互,内核程序然后执行该请求。系统调用通常是读取和写入文件、联网和启动新进程等操作所必需的。
当 执行系统调用时,内核需要访问其自己的内存和调用进程的内存。因为正在执行当前线程的处理器被配置为使用地址空间映射来为当前进程映射虚拟地址,所以大部 分操作系统将每个进程地址空间的一部分映射到一个通用的内核内存区域。被映射来供内核使用的地址空间部分称为内核空间,其余部分称为用户空间,可供用户应 用程序使用。
内核空间和用户空间之间的平衡关系因操作系统的不同而不同,甚至在运行于不同硬件架构之上的同一操作系统的各个实例 间也有所不同。这种平衡通常是可配置的,可进行调整来为用户应用程序或内核提供更多空间。缩减内核区域可能导致一些问题,比如能够同时登录的用户数量限制 或能够运行的进程数量限制。更小的用户空间意味着应用程序编程人员只能使用更少的内存空间。
默认情况下,32 位 Windows 拥有 2GB 用户空间和 2GB 内核空间。在一些 Windows 版本上,通过向启动配置添加 /3GB 开关并使用/LARGEADDRESSAWARE 开关重新链接应用程序,可以将这种平衡调整为 3GB 用户空间和 1GB 内核空间。在 32 位 Linux 上,默认设置为 3GB 用户空间和 1GB 内核空间。一些 Linux 分发版提供了一个 hugemem 内核,支持 4GB 用户空间。为了实现这种配置,将进行系统调用时使用的地址空间分配给内核。通过这种方式增加用户空间会减慢系统调用,因为每次进行系统调用时,操作系统必 须在地址空间之间复制数据并重置进程地址-空间映射。图 2 展示了 32 位 Windows 的地址-空间布局:

31 位 Linux 390 上还使用了一个独立的内核地址空间,其中较小的 2GB 地址空间使对单个地址空间进行划分不太合理,但是,390 架构可以同时使用多个地址空间,而且不会降低性能。
进 程空间必须包含程序需要的所有内容,包括程序本身和它使用的共享库(在 Windows 上为 DDL,在 Linux 上为 .so 文件)。共享库不仅会占据空间,使程序无法在其中存储数据,它们还会使地址空间碎片化,减少可作为连续内存块分配的内存。这对于在拥有 3GB 用户空间的 Windows x86 上运行的程序尤为明显。DLL 在构建时设置了首选的加载地址:当加载 DLL 时,它被映射到处于特定位置的地址空间,除非该位置已经被占用,在这种情况下,它会加载到别处。Windows NT 最初设计时设置了 2GB 可用用户空间,这对于要构建来加载接近 2GB 区域的系统库很有用 —— 使大部分用户区域都可供应用程序自由使用。当用户区域扩展到 3GB 时,系统共享库仍然加载接近 2GB 数据(约为用户空间的一半)。尽管总体用户空间为 3GB,但是不可能分配 3GB 大的内存块,因为共享库无法加载这么大的内存。
在 Windows 中使用 /3GB 开关,可以将内核空间减少一半,也就是最初设计的大小。在一些情形下,可能耗尽 1GB 内核空间,使 I/O 变得缓慢,且无法正常创建新的用户会话。尽管 /3GB 开关可能对一些应用程序非常有用,但任何使用它的环境在部署之前都应该进行彻底的负载测试。参见 参考资料,获取关于 /3GB 开关及其优缺点的更多信息的链接。
本 机内存泄漏或过度使用本机内存将导致不同的问题,具体取决于您是耗尽了地址空间还是用完了物理内存。耗尽地址空间通常只会发生在 32 位进程上,因为最大 4GB 的内存很容易分配完。64 位进程具有数百或数千 GB 的用户空间,即使您特意消耗空间也很难耗尽这么大的空间。如果您确实耗尽了 Java 进程的地址空间,那么 Java 运行时可能会出现一些陌生现象,本文稍后将详细讨论。当在进程地址空间比物理内存大的系统上运行时,内存泄漏或过度使用本机内存会迫使操作系统交换后备存 储器来用作本机进程的虚拟地址空间。访问经过交换的内存地址比读取驻留(在物理内存中)的地址慢得多,因为操作系统必须从硬盘驱动器拉取数据。可能会分配 大量内存来用完所有物理内存和所有交换内存(页面空间),在 Linux 上,这将触发内核内存不足(OOM)结束程序,强制结束最消耗内存的进程。在 Windows 上,与地址空间被占满时一样,内存分配将会失败。
同时,如果尝试使用比物理内存大的虚拟内存,显然在进程由于消 耗内存太大而被结束之前就会遇到问题。系统将变得异常缓慢,因为它会将大部分时间用于在内存与交换空间之间来回复制数据。当发生这种情况时,计算机和独立 应用程序的性能将变得非常糟糕,从而使用户意识到出现了问题。当 JVM 的 Java 堆被交换出来时,垃圾收集器的性能会变得非常差,应用程序可能被挂起。如果一台机器上同时使用了多个 Java 运行时,那么物理内存必须足够分配给所有 Java 堆。

Java 运行时如何使用本机内存
Java 运行时是一个操作系统进程,它会受到我在上一节中列出的硬件和操作系统局限性的限制。运行时环境提供的功能受一些未知的用户代码驱动,这使得无法预测在每 种情形中运行时环境将需要何种资源。Java 应用程序在托管 Java 环境中执行的每个操作都会潜在地影响提供该环境的运行时的需求。本节描述 Java 应用程序为什么和如何使用本机内存。
Java 堆和垃圾收集
Java 堆是分配了对象的内存区域。大多数 Java SE 实现都拥有一个逻辑堆,但是一些专家级 Java 运行时拥有多个堆,比如实现 Java 实时规范(Real Time Specification for Java,RTSJ)的运行时。一个物理堆可被划分为多个逻辑扇区,具体取决于用于管理堆内存的垃圾收集(GC)算法。这些扇区通常实现为连续的本机内存 块,这些内存块受 Java 内存管理器(包含垃圾收集器)控制。
堆的大小可以在 Java 命令行使用 -Xmx 和 -Xms 选项来控制(mx 表示堆的最大大小,ms 表示初始大小)。尽管逻辑堆(经常被使用的内存区域)可以根据堆上的对象数量和在 GC 上花费的时间而增大和缩小,但使用的本机内存大小保持不变,而且由 -Xmx 值(最大堆大小)指定。大部分 GC 算法依赖于被分配为连续的内存块的堆,因此不能在堆需要扩大时分配更多本机内存。所有堆内存必须预先保留。
保留本机内存与分配本机内存不同。当本机内存被保留时,无法使用物理内存或其他存储器作为备用内存。尽管保留地址空间块不会耗尽物理资源,但会阻止内存被用于其他用途。由保留从未使用的内存导致的泄漏与泄漏分配的内存一样严重。
当使用的堆区域缩小时,一些垃圾收集器会回收堆的一部分(释放堆的后备存储空间),从而减少使用的物理内存。
对于维护 Java 堆的内存管理系统,需要更多本机内存来维护它的状态。当进行垃圾收集时,必须分配数据结构来跟踪空闲存储空间和记录进度。这些数据结构的确切大小和性质因实现的不同而不同,但许多数据结构都与堆大小成正比。
即时 (JIT) 编译器
JIT 编译器在运行时编译 Java 字节码来优化本机可执行代码。这极大地提高了 Java 运行时的速度,并且支持 Java 应用程序以与本机代码相当的速度运行。
字节码编译使用本机内存(使用方式与 gcc 等静态编译器使用内存来运行一样),但 JIT 编译器的输入(字节码)和输出(可执行代码)必须也存储在本机内存中。包含多个经过 JIT 编译的方法的 Java 应用程序会使用比小型应用程序更多的本机内存。
类和类加载器
Java 应用程序由一些类组成,这些类定义对象结构和方法逻辑。Java 应用程序也使用 Java 运行时类库(比如 java.lang.String)中的类,也可以使用第三方库。这些类需要存储在内存中以备使用。
存 储类的方式取决于具体实现。Sun JDK 使用永久生成(permanent generation,PermGen)堆区域。Java 5 的 IBM 实现会为每个类加载器分配本机内存块,并将类数据存储在其中。现代 Java 运行时拥有类共享等技术,这些技术可能需要将共享内存区域映射到地址空间。要理解这些分配机制如何影响您 Java 运行时的本机内存占用,您需要查阅该实现的技术文档。然而,一些普遍的事实会影响所有实现。
从最基本的层面来看,使用更多的类将 需要使用更多内存。(这可能意味着您的本机内存使用量会增加,或者您必须明确地重新设置 PermGen 或共享类缓存等区域的大小,以装入所有类)。记住,不仅您的应用程序需要加载到内存中,框架、应用服务器、第三方库以及包含类的 Java 运行时也会按需加载并占用空间。
Java 运行时可以卸载类来回收空间,但是只有在非常严酷的条件下才会这样做。不能卸载单个类,而是卸载类加载器,随其加载的所有类都会被卸载。只有在以下情况下才能卸载类加载器:
Java 堆不包含对表示该类加载器的 java.lang.ClassLoader 对象的引用。
Java 堆不包含对表示类加载器加载的类的任何 java.lang.Class 对象的引用。
在 Java 堆上,该类加载器加载的任何类的所有对象都不再存活(被引用)。
需要注意的是,Java 运行时为所有 Java 应用程序创建的 3 个默认类加载器( bootstrap、extension 和 application )都不可能满足这些条件,因此,任何系统类(比如 java.lang.String)或通过应用程序类加载器加载的任何应用程序类都不能在运行时释放。
即使类加载器适合进行收集,运行时也只会将收集类加载器作为 GC 周期的一部分。一些实现只会在某些 GC 周期中卸载类加载器。
也 可能在运行时生成类,而不用释放它。许多 JEE 应用程序使用 JavaServer Pages (JSP) 技术来生成 Web 页面。使用 JSP 会为执行的每个 .jsp 页面生成一个类,并且这些类会在加载它们的类加载器的整个生存期中一直存在 —— 这个生存期通常是 Web 应用程序的生存期。
另一种生成类的常见方法是使用 Java 反射。反射的工作方式因 Java 实现的不同而不同,但 Sun 和 IBM 实现都使用了这种方法,我马上就会讲到。
当使用 java.lang.reflect API 时,Java 运行时必须将一个反射对象(比如 java.lang.reflect.Field) 的方法连接到被反射到的对象或类。这可以通过使用 Java 本机接口(Java Native Interface,JNI)访问器来完成,这种方法需要的设置很少,但是速度缓慢。也可以在运行时为您想要反射到的每种对象类型动态构建一个类。后一种 方法在设置上更慢,但运行速度更快,非常适合于经常反射到一个特定类的应用程序。
Java 运行时在最初几次反射到一个类时使用 JNI 方法,但当使用了若干次 JNI 方法之后,访问器会膨胀为字节码访问器,这涉及到构建类并通过新的类加载器进行加载。执行多次反射可能导致创建了许多访问器类和类加载器。保持对反射对象 的引用会导致这些类一直存活,并继续占用空间。因为创建字节码访问器非常缓慢,所以 Java 运行时可以缓存这些访问器以备以后使用。一些应用程序和框架还会缓存反射对象,这进一步增加了它们的本机内存占用。
JNI
JNI 支持本机代码(使用 C 和 C++ 等本机编译语言编写的应用程序)调用 Java 方法,反之亦然。Java 运行时本身极大地依赖于 JNI 代码来实现类库功能,比如文件和网络 I/O。JNI 应用程序可能通过 3 种方式增加 Java 运行时的本机内存占用:
JNI 应用程序的本机代码被编译到共享库中,或编译为加载到进程地址空间中的可执行文件。大型本机应用程序可能仅仅加载就会占用大量进程地址空间。

本机代码必须与 Java 运行时共享地址空间。任何本机代码分配或本机代码执行的内存映射都会耗用 Java 运行时的内存。

某些 JNI 函数可能在它们的常规操作中使用本机内存。GetTypeArrayElements 和 GetTypeArrayRegion 函数可以将 Java 堆数据复制到本机内存缓冲区中,以供本机代码使用。是否复制数据依赖于运行时实现。(IBM Developer Kit for Java 5.0 和更高版本会进行本机复制)。通过这种方式访问大量 Java 堆数据可能会使用大量本机堆。

⑦ Java中如何在无参构造方法中调用有参构造方法,希望最好有个例子,谢谢

一般正常的都是参数多的调用参数少的。有参数的调用无参数的居多。

当然你要无参调用的参的也可以。

你用无参,调用有参的。那你有参的参数你需要怎么传呢。当然如果换一个角度也可以。

我手写下。没经过IDE的。提供下思路。我有两种思路,写同一个功能。

public class Person{

private String name;

private int age;

//无参

public Person(){

this.name="张三";

this.age=20;

}

//有参

public Person(String name){

this.name=name;

}

//多参

public Person(String name,int age){

this(name);//调用一个参数的构造方法

this.age=age;

}

}

这是一种写法比较常用的方法。写第一种。用上面的例子直接写

public Person(){

this("张三",20);//调用有参构造方法。设置默认值。和第一种方法功能一样

}

public Person(String name,int age){

this.name=name;

this.age=age;

}

为了程序易读性。一般。参数的扩展和构造方法的调用都是有一定的规律的。

有参调无参,多参调少参。。扩展参数的时候,最好保持原来的顺序。

⑧ JVM原理是什么

首先这里澄清两个概念:JVM实例和JVM执行引擎实例,JVM实例对应了一个独立运行的Java程序,而JVM执行引擎实例则对应了属于用户运行程序的线程;也就是JVM实例是进程级别,而执行引擎是线程级别的。JVM是什么?—JVM的生命周期JVM实例的诞生:当启动一个Java程序时,一个JVM实例就产生了,任何一个拥有publicstaticvoidmain(String[]args)函数的class都可以作为JVM实例运行的起点,既然如此,那么JVM如何知道是运行classA的main而不是运行classB的main呢?这就需要显式的告诉JVM类名,也就是我们平时运行Java程序命令的由来,如JavaclassAhelloworld,这里Java是告诉os运行SunJava2SDK的Java虚拟机,而classA则指出了运行JVM所需要的类名。JVM实例的运行:main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,Java程序也可以标明自己创建的线程是守护线程。JVM实例的消亡:当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。JVM是什么?—JVM的体系结构粗略分来,JVM的内部体系结构分为三部分,分别是:类装载器(ClassLoader)子系统,运行时数据区,和执行引擎。下面将先介绍类装载器,然后是执行引擎,最后是运行时数据区1、类装载器,顾名思义,就是用来装载.class文件的。JVM的两种类装载器包括:启动类装载器和用户自定义类装载器,启动类装载器是JVM实现的一部分,用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。(下面所述情况是针对SunJDK1.2)动类装载器:只在系统类(JavaAPI的类文件)的安装路径查找要装入的类用户自定义类装载器:系统类装载器:在JVM启动时创建,用来在CLASSPATH目录下查找要装入的类其他用户自定义类装载器:这里有必要先说一下ClassLoader类的几个方法,了解它们对于了解自定义类装载器如何装载.class文件至关重要。(Stringname,bytedata[],intoffset,intlength) (Stringname,bytedata[],intoffset,intlength,);(Stringname) (Classc) defineClass用来将二进制class文件(新类型)导入到方法区,也就是这里指的类是用户自定义的类(也就是负责装载类)findSystemClass通过类型的全限定名,先通过系统类装载器或者启动类装载器来装载,并返回Class对象。ResolveClass:让类装载器进行连接动作(包括验证,分配内存初始化,将类型中的符号引用解析为直接引用),这里涉及到Java命名空间的问题,JVM保证被一个类装载器装载的类所引用的所有类都被这个类装载器装载,同一个类装载器装载的类之间可以相互访问,但是不同类装载器装载的类看不见对方,从而实现了有效的屏蔽。2、执行引擎:它或者在执行字节码,或者执行本地方法要说执行引擎,就不得不的指令集,每一条指令包含一个单字节的操作码,后面跟0个或者多个操作数。(一)指令集以栈为设计中心,而非以寄存器为中心这种指令集设计如何满足Java体系的要求:平台无关性:以栈为中心使得在只有很少register的机器上实现Java更便利compiler一般采用stack向连接优化器传递编译的中间结果,若指令集以stack为基础,则有利于运行时进行的优化工作与执行即时编译或者自适应优化的执行引擎结合,通俗的说就是使编译和运行用的数据结构统一,更有利于优化的开展。网络移动性:class文件的紧凑性。安全性:指令集中绝大部分操作码都指明了操作的类型。(在装载的时候使用数据流分析期进行一次性验证,而非在执行每条指令的时候进行验证,有利于提高执行速度)。(二)执行技术主要的执行技术有:解释,即时编译,自适应优化、芯片级直接执行其中解释属于第一代JVM,即时编译JIT属于第二代JVM,自适应优化(目前Sun的HotspotJVM采用这种技术)则吸取第一代JVM和第二代JVM的经验,采用两者结合的方式自适应优化:开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。3、运行时数据区:主要包括:方法区,堆,Java栈,PC寄存器,本地方法栈(1)方法区和堆由所有线程共享堆:存放所有程序在运行时创建的对象方法区:当JVM的类装载器加载.class文件,并进行解析,把解析的类型信息放入方法区。(2)Java栈和PC寄存器由线程独享,在新线程创建时间里(3)本地方法栈:存储本地方法调用的状态上边总体介绍了运行时数据区的主要内容,下边进行详细介绍,要介绍数据区,就不得不说明JVM中的数据类型。JVM中的数据类型:JVM中基本的数据单元是word,而word的长度由JVM具体的实现者来决定数据类型包括基本类型和引用类型,(1)基本类型包括:数值类型(包括除boolean外的所有的Java基本数据类型),boolean(在JVM中使用int来表示,0表示false,其他int值均表示true)和returnAddress(JVM的内部类型,用来实现finally子句)。(2)引用类型包括:数组类型,类类型,接口类型前边讲述了JVM中数据的表示,下面让我们输入到JVM的数据区首先来看方法区:上边已经提到,方法区主要用来存储JVM从class文件中提取的类型信息,那么类型信息是如何存储的呢?众所周知,Java使用的是大端序(big?endian:即低字节的数据存储在高位内存上,如对于1234,12是高位数据,34为低位数据,则Java中的存储格式应该为12存在内存的低地址,34存在内存的高地址,x86中的存储格式与之相反)来存储数据,这实际上是在class文件中数据的存储格式,但是当数据倒入到方法区中时,JVM可以以任何方式来存储它。类型信息:包括class的全限定名,class的直接父类,类类型还是接口类型,类的修饰符(public,等),所有直接父接口的列表,Class对象提供了访问这些信息的窗口(可通过Class.forName(“”)或instance.getClass()获得),下面是Class的方法,相信大家看了会恍然大悟,(原来如此J)getName(),getSuperClass(),isInterface(),getInterfaces(),getClassLoader();static变量作为类型信息的一部分保存指向ClassLoader类的引用:在动态连接时装载该类中引用的其他类指向Class类的引用:必然的,上边已述该类型的常量池:包括直接常量(String,integer和floatpoint常量)以及对其他类型、字段和方法的符号引用(注意:这里的常量池并不是普通意义上的存储常量的地方,这些符号引用可能是我们在编程中所接触到的变量),由于这些符号引用,使得常量池成为Java程序动态连接中至关重要的部分字段信息:普通意义上的类型中声明的字段方法信息:类型中各个方法的信息编译期常量:指用final声明或者用编译时已知的值初始化的类变量class将所有的常量复制至其常量池或者其字节码流中。方法表:一个数组,包括所有它的实例可能调用的实例方法的直接引用(包括从父类中继承来的)除此之外,若某个类不是抽象和本地的,还要保存方法的字节码,操作数栈和该方法的栈帧,异常表。举例:classLava{ privateintspeed=5; voidflow(){} classVolcano{ publicstaticvoidmain(String[]args){ Lavalava=newLava(); lava.flow(); } } 运行命令JavaVolcano;(1)JVM找到Volcano.class倒入,并提取相应的类型信息到方法区。通过执行方法区中的字节码,JVM执行main()方法,(执行时会一直保存指向Vocano类的常量池的指针)(2)Main()中第一条指令告诉JVM需为列在常量池第一项的类分配内存(此处再次说明了常量池并非只存储常量信息),然后JVM找到常量池的第一项,发现是对Lava类的符号引用,则检查方法区,看Lava类是否装载,结果是还未装载,则查找“Lava.class”,将类型信息写入方法区,并将方法区Lava类信息的指针来替换Volcano原常量池中的符号引用,即用直接引用来替换符号引用。(3)JVM看到new关键字,准备为Lava分配内存,根据Volcano的常量池的第一项找到Lava在方法区的位置,并分析需要多少对空间,确定后,在堆上分配空间,并将speed变量初始为0,并将lava对象的引用压到栈中(4)调用lava的flow()方法好了,大致了解了方法区的内容后,让我们来看看堆Java对象的堆实现:Java对象主要由实例变量(包括自己所属的类和其父类声明的)以及指向方法区中类数据的指针,指向方法表的指针,对象锁(非必需),等待集合(非必需),GC相关的数据(非必需)(主要视GC算法而定,如对于标记并清除算法,需要标记对象是否被引用,以及是否已调用finalize()方法)。那么为什么Java对象中要有指向类数据的指针呢?我们从几个方面来考虑首先:当程序中将一个对象引用转为另一个类型时,如何检查转换是否允许?需用到类数据其次:动态绑定时,并不是需要引用类型,而是需要运行时类型,这里的迷惑是:为什么类数据中保存的是实际类型,而非引用类型?这个问题先留下来,我想在后续的读书笔记中应该能明白指向方法表的指针:这里和C++的VTBL是类似的,有利于提高方法调用的效率对象锁:用来实现多个线程对共享数据的互斥访问等待集合:用来让多个线程为完成共同目标而协调功过。(注意Object类中的wait(),notify(),notifyAll()方法)。Java数组的堆实现:数组也拥有一个和他们的类相关联的Class实例,具有相同dimension和type的数组是同一个类的实例。数组类名的表示:如[[LJava/lang/Object表示Object[][],[I表示int[],[[[B表示byte[][][]至此,堆已大致介绍完毕,下面来介绍程序计数器和Java栈程序计数器:为每个线程独有,在线程启动时创建,若thread执行Java方法,则PC保存下一条执行指令的地址。若thread执行native方法,则Pc的值为undefinedJava栈:Java栈以帧为单位保存线程的运行状态,Java栈只有两种操作,帧的压栈和出栈。每个帧代表一个方法,Java方法有两种返回方式,return和抛出异常,两种方式都会导致该方法对应的帧出栈和释放内存。帧的组成:局部变量区(包括方法参数和局部变量,对于instance方法,还要首先保存this类型,其中方法参数按照声明顺序严格放置,局部变量可以任意放置),操作数栈,帧数据区(用来帮助支持常量池的解析,正常方法返回和异常处理)。本地方法栈:依赖于本地方法的实现,如某个JVM实现的本地方法借口使用C连接模型,则本地方法栈就是C栈,可以说某线程在调用本地方法时,就进入了一个不受JVM限制的领域,也就是JVM可以利用本地方法来动态扩展本身。相信大家都明白JVM是什么了吧。原文链接: http://www.cnblogs.com/chenzhao/archive/2011/08/14/2137713.html

⑨ jvm的理解

1
JVM内存区域

我们在编写程序时,经常会遇到OOM(out of Memory)以及内存泄漏等问题。为了避免出现这些问题,我们首先必须对JVM的内存划分有个具体的认识。JVM将内存主要划分为:方法区、虚拟机栈、本地方法栈、堆、程序计数器。JVM运行时数据区如下:

1.1
程序计数器
程序计数器是线程私有的区域,很好理解嘛~,每个线程当然得有个计数器记录当前执行到那个指令。占用的内存空间小,可以把它看成是当前线程所执行的字节码的行号指示器。如果线程在执行Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的是Native方法,这个计数器的值为空(Undefined)。

此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

1.2
Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,其生命周期与线程相同。

如何理解虚拟机栈呢?

本质上来讲,就是个栈。里面存放的元素叫栈帧,栈帧好像很复杂的样子,其实它很简单!它里面存放的是一个函数的上下文,具体存放的是执行的函数的一些数据。执行的函数需要的数据无非就是局部变量表(保存函数内部的变量)、操作数栈(执行引擎计算时需要),方法出口等等。

执行引擎每调用一个函数时,就为这个函数创建一个栈帧,并加入虚拟机栈。换个角度理解,每个函数从调用到执行结束,其实是对应一个栈帧的入栈和出栈。

注意这个区域可能出现的两种异常:

一种是StackOverflowError,当前线程请求的栈深度大于虚拟机所允许的深度时,会抛出这个异常。制造这种异常很简单:将一个函数反复递归自己,最终会出现栈溢出错误(StackOverflowError)。

另一种异常是OutOfMemoryError异常,当虚拟机栈可以动态扩展时(当前大部分虚拟机都可以),如果无法申请足够多的内存就会抛出OutOfMemoryError,如何制作虚拟机栈OOM呢,参考一下代码:

这段代码有风险,可能会导致操作系统假死,请谨慎使用~~~

1.3
本地方法栈
本地方法栈与虚拟机所发挥的作用很相似,他们的区别在于虚拟机栈为执行Java代码方法服务,而本地方法栈是为Native方法服务。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

1.4
Java堆
Java堆可以说是虚拟机中最大一块内存了。它是所有线程所共享的内存区域,几乎所有的实例对象都是在这块区域中存放。当然,随着JIT编译器的发展,所有对象在堆上分配渐渐变得不那么“绝对”了。

Java堆是垃圾收集器管理的主要区域。由于现在的收集器基本上采用的都是分代收集算法,所有Java堆可以细分为:新生代和老年代。在细致分就是把新生代分为:Eden空间、From Survivor空间、To Survivor空间。当堆无法再扩展时,会抛出OutOfMemoryError异常。

1.5
方法区
方法区存放的是类信息、常量、静态变量等。方法区是各个线程共享区域,很容易理解,我们在写Java代码时,每个线程度可以访问同一个类的静态变量对象。由于使用反射机制的原因,虚拟机很难推测那个类信息不再使用,因此这块区域的回收很难。另外,对这块区域主要是针对常量池回收,值得注意的是JDK1.7已经把常量池转移到堆里面了。同样,当方法区无法满足内存分配需求时,会抛出OutOfMemoryError。

制造方法区内存溢出,注意,必须在JDK1.6及之前版本才会导致方法区溢出,原因后面解释,执行之前,可以把虚拟机的参数-XXpermSize和-XX:MaxPermSize限制方法区大小。

运行后会抛出java.lang.OutOfMemoryError:PermGen space异常。

解释一下,String的intern()函数作用是如果当前的字符串在常量池中不存在,则放入到常量池中。上面的代码不断将字符串添加到常量池,最终肯定会导致内存不足,抛出方法区的OOM。

下面解释一下,为什么必须将上面的代码在JDK1.6之前运行。我们前面提到,JDK1.7后,把常量池放入到堆空间中,这导致intern()函数的功能不同,具体怎么个不同法,且看看下面代码:

这段代码在JDK1.6和JDK1.7运行的结果不同。

JDK1.6结果是:false,false ,JDK1.7结果是true, false。

原因是:JDK1.6中,intern()方法会吧首次遇到的字符串实例复制到常量池中,返回的也是常量池中的字符串的引用,而StringBuilder创建的字符串实例是在堆上面,所以必然不是同一个引用,返回false。

在JDK1.7中,intern不再复制实例,常量池中只保存首次出现的实例的引用,因此intern()返回的引用和由StringBuilder创建的字符串实例是同一个。为什么对str2比较返回的是false呢?这是因为,JVM中内部在加载类的时候,就已经有"java"这个字符串,不符合“首次出现”的原则,因此返回false。

⑩ 怎样在ide中进行jvm源码的调试

开发调试环境: Ubuntu 14.10,openJdk 1.7,Android Studio 1.0.2,android-5.0.1_r1源码
由于AS是基于IntelliJ IDEA开发的,所以本文也适用于IntelliJ IDEA
一、修改Android Studio(以下简称AS)的内存配置

因为在导入源码时需要消耗大量内存,所以先修改IDEA_HOME/bin/studio64.vmoptions(x86的机器修改studio.vmoptions)中-Xms和-Xmx的值。文档中使用的是748m, 可自行修改。
二、配置AS的JDK、SDK
在IDE中添加一个没有classpath的JDK, 这样可以确保使用源码里的库文件

并将其作为要使用的SDK的Java SDK。如下图

三、生成导入AS所需配置文件(*.ipr)
①编译源码(为了确保生成了.java文件,如R.java;如果编译过,则无需再次编译)
②检查out/host/linux-x86/framework/目录下是否有idegen.jar
如果idegen.jar不存在,执行:
mmm development/tools/idegen/

在5.0.1的源码中会生成res.java的文件夹,导致idegen.jar运行时抛FileNotFoundException,这是idegen的代码不够严谨造成的。
我的分享里有修改这个bug的patch,或者直接使用我分享的idegen.jar。
③执行
development/tools/idegen/idegen.sh

等待出现类似下面的结果:
Read excludes: 5ms
Traversed tree: 44078ms

这时会在源码的根目录下生成android.ipr和android.iml两个IntelliJ IDEA(AS是基于IntelliJ IDEA社区版开发的)的配置文件
Tips:
AS在导入代码时比较慢,建议先修改android.iml,将自己用不到的代码exclude出去.可以仿照过滤.repo文件夹的语法,如:
<excludeFolder url="file://$MODULE_DIR$/.repo" />
<excludeFolder url="file://$MODULE_DIR$/abi" />
<excludeFolder url="file://$MODULE_DIR$/art" />
这样在导入时就会跳过abi和art文件夹.过滤的越多,AS的处理速度就会越快.
④在AS中打开源码根目录下新生成的android.ipr

阅读全文

与jvm知识导图如何调用方法的相关的资料

热点内容
中式棉袄制作方法图片 浏览:57
五菱p1171故障码解决方法 浏览:852
男士修护膏使用方法 浏览:540
电脑图标修改方法 浏览:601
湿气怎么用科学的方法解释 浏览:532
910除以26的简便计算方法 浏览:799
吹东契奇最简单的方法 浏览:698
对肾脏有好处的食用方法 浏览:92
电脑四线程内存设置方法 浏览:508
数字电路通常用哪三种方法分析 浏览:9
实训课程的教学方法是什么 浏览:521
苯甲醇乙醚鉴别方法 浏览:78
苹果手机微信视频声音小解决方法 浏览:696
控制箱的连接方法 浏览:71
用什么简单的方法可以去痘 浏览:785
快速去除甲醛的小方法你知道几个 浏览:799
自行车架尺寸测量方法 浏览:120
石磨子的制作方法视频 浏览:148
行善修心的正确方法 浏览:401
薯仔炖鸡汤的正确方法和步骤 浏览:274