C++和Java的区别

指针:java中不存在指针的概念,编程者无法直接通过指针来直接访问内存,有利于维护java程序的安全

多重继承:C++支持多重继承,java不支持多重继承,但是允许一个类继承多个接口来实现多重继承的问题

数据类型和类:java是完全面向对象的语言,所有的函数和变量必须是类的一部分,而C++中允许将函数和变量设置为全局,兼具面向过程和面向对象的特点

内存管理:Java中由系统进行自动的内存管理和回收,C++中需要程序员手动释放内存资源。当Java中的一个对象不会再用到的时候,无用内存会给它贴上标签以示删除,Java的GC过程是以线程的形式在后台运行,利用空闲时间工作

操作符重载:Java不支持操作符重载,C++支持操作符重载,也是C++一个突出特征

预处理功能:Java不支持预处理,C++有一个预编译阶段,Java没有预处理器,但它提供了import与C++预处理器具有类似功能

Java不支持缺省函数参数

字符串:字符串变量

类型转换:C++中有数据类型隐含转换的机制,Java中需要限时强制类型转换

异常:Java中异常机制用于捕获例外事件,增强系统的容错能力

Java为解释型语言,程序源代码通过Java编译器编译成字节码,然后由jvm解释执行,C++为编译型语言,源代码通过编译链接后直接生成可执行的二进制代码,可直接执行,因此java的执行速度比C++要慢,但是Java可以跨平台

Java具有平台无关性,对每种数据类型都分配了固定长度的空间,但是C++不同,在不同的平台上会分配不同的字节数

Java跨平台的原因

第一点: 我们通常将CPU处理器和操作系统的整体称之为平台,不同的CPU中可能使用不同的指令集(指令集是CPU中用于计算和控制计算机系统的一套指令的集合),而不同操作系统支持不同CPU的指令集

C语言的编译过程:windows下通过VS编译成exe文件,Linux下通过gcc编译成elf文件,但是window所编译的exe是不能在linux上运行的,因此结论是:编译器是与平台相关的,编译后的文件也是与平台相关的,我们所说的跨平台是指编译后的文件跨平台,而不是源程序跨平台,这点要注意。

对于Java而言,源程序为.java文件,通过与平台无关的编译器编译成与平台无关的中间码,也就是.class文件,中间码再由解释器(也就是jvm)解释执行,注意解释器是与平台相关的,也就是不同平台需要不同的解释器。

JVM:Java虚拟机

1. 基本特性

JRE由Java api和JVM组成,JVM通过classloader来加载类应用,通过JavaAPI来执行

基于栈结构的虚拟机

符号引用:除了基本类型外所有的Java类型都是通过符号引用来取得关联,而不是通过显式内存地址的引用

垃圾回收机制:显式创建,通过GC自动回收

明确界定基本类型字节长度保证平台的无关性

网络字节序: 基于大端的字节序

2. Java程序的执行过程

类加载将Java字节码载入到运行时数据区,执行引擎负责Java字节码执行;

3. 类加载

Java提供了动态加载的特性,只有在运行时第一次遇到类时才会去加载和链接,而非在编译时加载它。JVM的类加载器负责类的动态加载过程。Java类加载器的特点如下:

层次结构:Java的类加载器按是父子关系的层次结构组织的。Boostrap类加载器处于层次结构的顶层,是所有类加载器的父类。

委派模式:基于类加载器的层次组织结构,类加载器之间是可以进行委派的。当一个类需要被加载,会先去请求父加载器判断该类是否已经被加载。如果父类加器已加载了该类,那它就可以直接使用而无需再次加载。如果尚未加载,才需要当前类加载器来加载此类。

可见性限制:子类加载器可以从父类加载器中获取类,反之则不行。

不能卸载: 类加载器可以载入类却不能卸载它。但是可以通过删除类加载器的方式卸载类。

每个类加载器都有自己的空间,用于存储其加载的类信息。当类加载器需要加载一个类时,它通过FQCN)(Fully Quanlified Class Name: 全限定类名)的方式先在自己的存储空间中检测此类是否已存在。在JVM中,即便具有相同FQCN的类,如果出现在了两个不同的类加载器空间中,它们也会被认为是不同的。存在于不同的空间意味着类是由不同的加载器加载的。

当JVM请示类加载器加载一个类时,加载器总是按照从类加载器缓存、父类加载器以及自己加载器的顺序查找和加载类。也就是说加载器会先从缓存中判断此类是否已存在,如果不存在就请示父类加载器判断是否存在,如果直到Bootstrap类加载器都不存在该类,那么当前类加载器就会从文件系统中找到类文件进行加载。

Bootstrap加载器:Bootstrap加载器在运行JVM时创建,用于加载Java APIs,包括Object类。不像其他的类加载器由Java代码实现,Bootstrap加载器是由native代码实现的。

扩展加载器(Extension class loader):扩展加载器用于加载除基本Java APIs以外扩展类。也用于加载各种安全扩展功能。

系统加载器(System class loader):如果说Bootstrap和Extension加载器用于加载JVM运行时组件,那么系统加载器加载的则是应用程序相关的类。它会加载用户指定的CLASSPATH里的类。

用户自定义加载器:这个是由用户的程序代码创建的类加载器。

像Web应用服务器(WAS: Web Application Server)等框架通过使用用户自定义加载器使Web应用和企业级应用可以隔离开在各自的类加载空间独自运行。也就是说可以通过类加载器的委派模式来保证应用的独立性。不同的WAS在自定义类加载器时会有略微不同,但都不外乎使用加载器的层次结构原理。

如果一个类加载器发现了一个未加载的类,则该类的加载和链接过程如下图

每一步的具体描述如下:

加载(Loading): 从文件中获取类并载入到JVM内存空间。

验证(Verifying): 验证载入的类是否符合Java语言规范和JVM规范。在类加载流程的测试过程中,这一步是最为复杂且耗时最长的部分。大部分JVM TCK的测试用例都用于检测对于给定的错误的类文件是否能得到相应的验证错误信息。

准备(Preparing): 根据内存需求准备相应的数据结构,并分别描述出类中定义的字段、方法以及实现的接口信息。

解析(Resolving): 把类常量池中所有的符号引用转为直接引用。

初始化(Initializing): 为类的变量初始化合适的值。执行静态初始化域,并为静态字段初始化相应的值。

4. 运行时数据区

运行时数据区是JVM运行时操作系统分配的内存区域,运行时数据区可分为6部分,即:为每个线程分别创建的PC寄存器,JVM栈,本地方法栈,和被所有线程共用的数据堆,方法区,和运行时常量池。

PC寄存器:每一个线程都会有一个Program counter寄存器,随着线程启动而创建,其中存放要执行的JVM指令地址;

JVM栈:每一个线程都会有一个JVM栈,随着线程启动而创建,其中存储的数据元素为栈帧,在JVM中一旦有方法执行,JVM都会为之创建一个栈帧,并添加到当前现成的JVM栈中,当方法运行结束后,栈帧也会随之移除。栈帧中保存着对本地变量数组,操作数栈和属于当前运行方法的运行时常量池的引用。

本地方法栈:为非Java编写的本地程序定义的栈空间,也就是说它基本上是用于通过JNI(Java Native Interface)方式调用和执行的C/C++代码。根据具体情况,C栈或C++栈将会被创建。

方法区:方法区是被所有线程共用的内存空间,在JVM启动时创建。它存储了运行时常量池、字段和方法信息、静态变量以及被JVM载入的所有类和接口的方法的字节码。不同的JVM提供者在实现方法区时会通常有不同的形式。在Oracle的Hotspot JVM里方法区被称为Permanent Area(永久区)或Permanent Generation(PermGen, 永久代)。JVM规范并对方法区的垃圾回收未做强制限定,因此对于JVM实现者来说,方法区的垃圾回收是可选操作。

运行时常量池:一个存储了类文件格式中的常量池表的内存空间。这部分空间虽然存在于方法区内,但却在JVM操作中扮演着举足轻重的角色,因此JVM规范单独把这一部分拿出来描述。除了每个类或接口中定义的常量,它还包含了所有对方法和字段的引用。因此当需要一个方法或字段时,JVM通过运行时常量池中的信息从内存空间中来查找其相应的实际地址。

数据堆:堆中存储着所有的类实例或对象,并且也是垃圾回收的目标场所。当涉及到JVM性能优化时,通常也会提及到数据堆空间的大小设置。JVM提供者可以决定划分堆空间或者不执行垃圾回收。

5. 执行引擎

JVM通过类加载器把字节码载入运行时数据区是由执行引擎执行的。执行引擎以指令为单位读入Java字节码,就像CPU一个接一个的执行机器命令一样。每个字节码命令包含一字节的操作码和可选的操作数。执行引擎读取一个指令并执行相应的操作数,然后去读取并执行下一条指令。

尽管如此,Java字节码还是以一种可以理解的语言编写的,而不像那些机器直接执行的无法读懂的语言。所以JVM的执行引擎必须要把字节码转换为能被机器执行的语言指令。执行引擎有两种常用的方法来完成这一工作:

解释器(Interpreter):读取、解释并逐一执行每一条字节码指令。因为解释器逐一解释和执行指令,因此它能够快速的解释每一个字节码,但对解释结果的执行速度较慢。所有的解释性语言都有类似的缺点。叫做字节码的语言人本质上就像一个解释器一样运行。

即时编译器(JIT: Just-In-Time):即时编译器的引入用来弥补解释器的不足。执行引擎先以解释器的方式运行,然后在合适的时机,即时编译器把整修字节码编译成本地代码。然后执行引擎就不再解释方法的执行而是通过使用本地代码直接执行。执行本地代码较逐一解释执行每条指令在速度上有较大的提升,并且通过对本地代码的缓存,编译后的代码能具有更快的执行速度。

然而,即时编译器在编译代码时比逐一解释和执行每条指令更耗时,所以如果代码只会被执行一次,解释执行可能会具有更好的性能。所以JVM通过检查方法的执行频率,然后只对达到一定频率的方法才会做即时编译。

Java垃圾回收机制

1. 哪些内存需要回收?

引用计数法:判断是否还需要使用,最简单方法是通过目前是否有引用指向这个对象,如果没有说明这个对象就不会再使用了,这种通过引用是否存在的方法叫做引用计数法,但是存在一个问题无法解决就是对象循环引用问题。

可达性分析:这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

在Java语言中,可作为GC Roots的对象包括下面几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象。

方法区中类静态属性引用的对象。

方法区中常量引用的对象。

本地方法栈中JNI(即一般说的Native方法)引用的对象。

2 如何回收?

step 1: marking 标记

第一步是标记,也就是找到那些需要回收的对象和确定哪些对象不需要回收,所有堆中的对象都会扫描一遍,这通常是一个很耗时的过程。

step2 : normal deletion

垃圾收集器清除掉标记出来的对象区域,简单的清除带来的问题是产生大量的不连续的内存碎片,空间碎片太多可能会导致在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而提前触发一次垃圾回收

step 2 :improve -- deletion with compacting 压缩整理

由于简单的清除可能会存在碎片的问题,所以又出现了压缩清除的方法,也就是先清除需要回收的对象,然后再对内存进行压缩操作,将内存分成可用和不可用两大部分。

3 分代回收

为什么需要分代回收:一个程序中大部分对象都是短命的,因此为了增大GC的效率,将JVM堆分代,分为新生代,老年代和永久代。

新生代:所有new出来的新对象都在新生代,新生代这部分内存满了之后,就会发起一次GC事件,这种发生在新生代的垃圾回收称为Minor collections,这种收集相对比较快。

老年代:老年代来存储存活事件比较长的对象,一般来说,我们会给新生代的对象限定一个存活的时间,当达到这个时间还没有被收集的时候就会被移动到老年代中。老年代区域的垃圾收集叫做major garbage collection。通常Major garbage collection都相对比较慢,因为老年代的收集包括了对所有对象的收集,也就是同时需要收集新生代和老年代的对象。

永久代:The Permanent generation contains metadata required by the JVM to describe the classes and methods used in the application. The permanent generation is populated by the JVM at runtime based on classes in use by the application. In addition, Java SE library classes and methods may be stored here.

5 分代回收的过程

第一步 所有new出来的对象都会最先分配到新生代区域中,两个survivor区域初始化是为空的

第二步,当eden区域满了之后,就引发一次 minor garbage collection

第三步,当在minor garbage collection,存活下来的对象就会被移动到S0survivor区域

第四步,然后当eden区域又填满的时候,又会发生下一次的垃圾回收,存活的对象会被移动到survivor区域而未存活对象会被直接删除。但是,不同的是,在这次的垃圾回收中,存活对象和之前的survivor中的对象都会被移动到s1中。一旦所有对象都被移动到s1中,那么s2中的对象就会被清除,仔细观察图中的对象,数字表示经历的垃圾收集的次数。目前我们已经有不同的年龄对象了。

第五步,下一次垃圾回收的时候,又会重复上次的步骤,清除需要回收的对象,并且又切换一次survivor区域,所有存活的对象都被移动至s0。eden和s1区域被清除。

第六步,重复以上步骤,并记录对象的年龄,当有对象的年龄到达一定的阈值的时候,就将新生代中的对象移动到老年代中。在本例中,这个阈值为8.

第七步,接下来垃圾收集器就会重复以上步骤,不断的进行对象的清除和年代的移动

最后,我们观察上述过程可以发现,大部分的垃圾收集过程都是在新生代进行的,直到老年代中的内存不够用了才会发起一次 major GC,会进行标记和整理压缩。

Java中对象的生命周期

创建阶段(created)

为对象分配存储空间,开始构造对象,从父类到子类对static成员进行初始化,父类成员变量按照顺序初始化,递归调用父类的构造方法,子类成员变量按照顺序初始化,子类构造方法调用,一旦对象被创建,并有某个引用指向它,这个对象的状态就切换到了应用阶段(In Use)

应用阶段(in use)

对象至少被一个强引用持有并且对象在作用域内

不可见阶段(Invisible)

程序本身不再持有该对象的任何强引用,但是这些引用可能还存在着;一般具体是指程序的执行已经超过该对象的作用域了

不可达阶段(Unreachable)

对象处于不可达阶段是指该对象不再被任何强引用所持有。

与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用,这种情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。

收集阶段(Collected)

当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。

这里要特别说明一下:不要重载finazlie()方法!原因有两点:

会影响JVM的对象分配与回收速度

在分配该对象时,JVM需要在垃圾回收器上注册该对象,以便在回收时能够执行该重载方法;在该方法的执行时需要消耗CPU时间且在执行完该方法后才会重新执行回收操作,即至少需要垃圾回收器对该对象执行两次GC。

可能造成该对象的再次“复活”

在finalize()方法中,如果有其它的强引用再次持有该对象,则会导致对象的状态由“收集阶段”又重新变为“应用阶段”。这个已经破坏了Java对象的生命周期进程,且“复活”的对象不利用后续的代码管理。

终结阶段

当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。

对象空间的重新分配

垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”。

java和jvm_java 和 JVM相关推荐

  1. java和jvm_Java、JVM和操作系统之间的关系,写给新人,

    来张图:这个帖子写给新人的,老玩家就直接无视他,因为这个完完全全是白话基础原理. 解释:上面的图是从上往下依次调用的关系. 操作系统(Windows/Linux)管理硬件,让硬件能够正常.合理的运行, ...

  2. java虚拟机jvm_java虚拟机jvm - zhuyuansj的个人空间 - OSCHINA - 中文开源技术交流社区...

    Java内存结构和java内存模型的区别.往往很多人会搞起来.这里主要对这2种进行解释并进行操作.经实践会JVM调优和不会JVM调优差别确实很大,足足可以提升服务器一倍的性能. java内存结构:所谓 ...

  3. java语音jvm_java环境中基于jvm的两大语言:scala,groovy

    一.java环境中基于jvm的两大语言:scala,groovy 可以在java项目里混编这两种语言: scala:静态语言,多范式语言,糅合了面向对象.面向过程:可以与java和net互操作:融汇了 ...

  4. 好程序员Java教程分享之jvm篇

    好程序员java教程分享之jvm篇,在前面的文章中,介绍了JVM内存模型分为:堆区.虚拟机栈.方法区.本地方法区和程序计数器,其中堆区是JVM中最大的一块内存区域,在Java中的所有对象实例都保存在此 ...

  5. Java 内存模型和 JVM 内存结构真不是一回事

    这两个概念估计有不少人会混淆,它们都可以说是 JVM 规范的一部分,但真不是一回事!它们描述和解决的是不同问题,简单来说, Java 内存模型,描述的是多线程允许的行为 JVM 内存结构,描述的是线程 ...

  6. java 利用ManagementFactory获取jvm,os的一些信息--转

    原文地址:http://blog.csdn.net/dream_broken/article/details/49759043 想了解下某个Java项目的运行时jvm的情况,可以使用一些监控工具,比如 ...

  7. 【Java虚拟机规范】JVM类加载机制

    [Java虚拟机规范]JVM类加载机制 理论知识 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading).验证(Verification).准备(Prep ...

  8. JVM运行时结构、Java内存管理、JVM实例、HotSpot VM对象的创建、内存布局和访问定位

    1.JVM运行时结构 Java 运行时数据区域有程序计数器.Java虚拟机栈.本地方法栈.Java堆和方法区.其中前三个线程私有,随线程生而生,线程灭而灭:后面两个是线程间共享. 1.1 程序计数器 ...

  9. java虚拟机_一文彻底读懂Java虚拟机!(JVM)

    提到Java虚拟机(JVM),可能大部分人的第一印象是"难",但当让我们真正走入"JVM世界"的时候,会发现其实问题并不像我们想象中的那么复杂.唯一真正令我们恐 ...

最新文章

  1. 菜鸟学java ——(一)面向对象程序设计(几个重要的概念)
  2. 利用Git Bash 远程访问服务器
  3. 最短路径算法(一) Dijkstra算法(贪心算法)
  4. C语言实现最长子序列 longest subsequence 算法(附完整源码)
  5. iftop 流量监控
  6. 使用JavaScript将当前页面保存成PDF,支持图片和文字的保存
  7. 《JavaScript 高级程序设计》笔记 第7章及以后
  8. SpringBoot 入门知识点详解
  9. 『Python CoolBook』Cython_高效数组操作
  10. 什么是Adam/ReLU/YOLO?这里有一份深度学习(.ai)词典
  11. 数控机床通信协议汇总
  12. ChIPQC——对ChIP-seq的质量评估
  13. std::string一个极其隐晦得问题
  14. Xenu's Link Sleuth 的使用
  15. 【Linux】进程终止与进程等待/僵尸进程与孤儿进程
  16. 交换机的背板容量、交换容量和包转发能力
  17. 数据挖掘课程笔记--关联分析
  18. ORACLE 根据分组排序产生序列号
  19. Ego的Spring框架笔记
  20. CSS 学习笔记总结(借鉴黑马程序员pink老师前端入门)

热门文章

  1. 学习理发去哪里_学习美发去哪里学
  2. 净迁移人口预测程序python_高质量深度学习模型, 一键模型预测,迁移学习很简单...
  3. 求解出n以内所有能被5整除的正整数的乘积_所有最常见最经典的算法题都在这里了...
  4. linux 在线帮助,linux获取在线帮助
  5. windows和ubuntu双系统设置开机默认系统
  6. 飞畅 Profibus总线光纤中继器产品介绍
  7. 【渝粤教育】国家开放大学2019年春季 1362应用语言学 参考试题
  8. 【渝粤题库】陕西师范大学300008 历史教学论
  9. 物联网为电信运营商带来众多商业机会
  10. pv实现前趋图_Excel 数据透视图实现简易交互式数据面板