手机搭建网站,查询二建证书注册在哪个单位,实惠的制作网站,做网站学什么语言好C和Java的区别指针#xff1a;java中不存在指针的概念#xff0c;编程者无法直接通过指针来直接访问内存#xff0c;有利于维护java程序的安全多重继承#xff1a;C支持多重继承#xff0c;java不支持多重继承#xff0c;但是允许一个类继承多个接口来实现多重继承的问题…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)解释执行注意解释器是与平台相关的也就是不同平台需要不同的解释器。JVMJava虚拟机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()方法后仍然处于不可达状态时则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。对象空间的重新分配垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了则该对象彻底消失了称之为“对象空间重新分配阶段”。