- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
本文讨论的JVM以JDK1.8为基准点,附带会横向比较,往前推到JDK1.6。JVM是任何一个学习JAVA的程序员绕不开的核心,本文就会围绕这个核心展开对它剖析,希望能给广大的程序员带来帮助.
Java Virtual Machine(Java虚拟机)的缩写 JVM是一个标准,一套规范,规定了.class文件在其内部运行的相关标准和规范,及其相关的内部构成。比如:所有的JVM都是基于栈结构的运行方式。那么不符合这种要求的,不算是JVM( 如Android中所使用的Dalvik 虚拟机就不能称作是JAVA 虚拟机, 因为它是基于寄存器(最新的Android系统据说已经放弃了Dalvik VM, 而是使用ART)。 JVM相关的产品有很多,现在最常用的是Oracle公司的HotSpot虚拟机(还有Oracle的JRockit、IBM的J9也是非常有名的JVM),HotSpot是JVM的具体实现。因此, 这里讨论的都是HotSpot虚拟机。 JVM是实现跨平台的关键,JVM在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因.
JDK: Java Development Kit JRE: Java Runtime Environment 。
JVM主要由以下四部分构成: 类加载器子系统( Class Loader)、 运行时数据区/内存空间 (Runtime Data Area)、 执行引擎 (Execution Engine) 本地接口/本地方法接口 (Native Interface) 。
。
类加载: 是通过JVM的类加载器从JVM外部以二进制字节流的方式加载到JVM中。 JVM本身有至少三种类加载器: 1). Bootstrap ClassLoader(根类加载器)C++实现, 加载位于 jre/lib/ 下的jar; 2). Extension ClassLoader(扩展类加载器)主要用于加载 jre/lib/ext/ 下的jar; 3). App ClassLoader(应用类加载器)加载classpath环境变量所指定的class; 4). Custom ClassLoader(自定义的类加载器)用于实现自己的类加载器, 如Tomcat中就实现多个类加载器,用来管理不同的jar。 类加载顺序:Custom → App → Extension → Bootstrap 直到这个类被加载成功 如果一个类被不同的类加载器加载, 那么就是两个不同的类.
为了保证类加载的安全性,在Java 1.2后引入了双亲委派模型 。
双亲委派: 1). 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtensionClassLoader去完成。 2). 当ExtensionClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootstrapClassLoader去完成。 3). 如果BootstrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtensionClassLoader来尝试加载; 4). 若ExtensionClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。 双亲委派模型有效解决了以下问题: 1). 每一个类都只会被加载一次,避免了重复加载 2). 每一个类都会被尽可能的加载(从引导类加载器往下,每个加载器都可能会根据优先次序尝试加载它) 3). 有效避免了某些恶意类的加载(比如自定义了Java。lang.Object类,一般而言在双亲委派模型下会加载系统的Object类而不是自定义的Object类) 。
。
。
。
1). 加载 : 首先,通过一个类的全类名来获取此类的二进制字节流;其次,将类中所代表的静态存储结构转换为运行时数据结构;最后,生成一个代表加载的类的java.lang.Class对象,作为方法区这个类的所有数据的访问入口。 加载完成之后,虚拟机外部的二进制静态数据结构就转换成了虚拟机所需要的结构存储在方法区中(至于如何转换,则由具体虚拟机自己定义实现),而所生成的Class对象,则存放在方法区中,用来作为程序访问方法区中数据的外部接口。 加载包括 隐式 加载(new方式创建对象)和 显式 加载(反射创建对象) 2). 验证 : 其目的就是保证加载进来的.class文件不会危害到虚拟机本身,且内容符合当前虚拟机的规范要求。 主要验证的内容大致有:文件格式验证、元数据验证、字节码验证、符号引用验证。 文件格式验证 : 主要确保符合class文件格式规范(如文本后缀为.class的文件将验证不通过),以及主次版本号,验证是否当前JVM可以处理等。 元数据验证 : 主要验证编译后的字节码描述信息是否符合java语法规范。 字节码验证 : 最为复杂,主要通过控制流和数据流确定语义是否合法、符合逻辑。 符号引用验证 : 可以看做是除自身以外(常量池中各种引用符号)的信息匹配校验,如通过持有的引用能否找到对应的实例。 3). 准备 : 包括类初始化(clinit)和对象实例化(init)。正式为类变量分配内存,并设置类变量的初始值。这些变量都会在方法区中进行分配。 4). 解析 : 将常量池内的符号引用替换为直接引用的过程。主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄等。 5). 初始化 : 加载的最后阶段,程序真正运行的开始。 6). 使用 7). 卸载 。
验证,准备,解析合称为链接 。
符号引用: 符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。 例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。 符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个Java类将会编译成一个class文件。在编译时,Java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中.
运行时数据区由五部分组成: 方法区、堆、虚拟机栈、本地方法栈、程序计数器 。
是线程共享,线程安全的。Java虚拟机规范中定义方法区是堆的一个逻辑部分。 方法区存储三种数据: 类信息(类及父类的完全限定名,类的类型,访问修饰符, Class引用、ClassLoader引用、实现的接口的全限定名的列表)、常量、静态变量。举例来说,如果两个类同时要加载一个尚未被加载的类,那么一个类会请求它的ClassLoader去加载需要的类,另一个类只能等待而不会重复加载。 3.1.1. 特点: 1>. 线程共享: 方法区是堆的一个逻辑部分,因此和堆一样,都是线程共享的。整个虚拟机中只有一个方法区。 2>. 永久代/元空间: 方法区中的信息一般需要长期存在,而且它又是堆的逻辑分区,因此用堆的划分方法,我们把方法区称为老年代。 3>. 内存回收效率低: 方法区中的信息一般需要长期存在,回收一遍内存之后可能只有少量信息无效。对方法区的内存回收的主要目标是:对常量池的回收和对类型的卸载。 4>. Java虚拟机规范对方法区的要求比较宽松: 和堆一样,允许固定大小,也允许可扩展的大小,还允许不实现垃圾回收。 3.1.2. 常量池 常量池主要分为:Class文件常量池、运行时常量池,字符串常量池。 注:包装类型常量池不属于JVM层面,而是java层面的封装。 Class文件常量池、运行时常量池 存储在方法区中 。
字符串常量池: JDK1.6: 存储在持久代中,与方法区隔离 JDK1.7: 存储在堆中 JDK1.8及以后: 存储在元空间,与方法区隔离 。
堆是所有线程共享的,用于存储对象实例、数组值、指向方法表的指针。 堆分为新生代(Minor)、旧生代(Major)、永久代(Permanent Space)[1.7及之前]、元数据区/元空间(Meta Space)[1.8及之后] 新生代分为Eden Space(伊甸园区)和Survivor Space(幸存者区): 由From Space和To Space组成 默认比例: Young : Old = 1:2, Eden : Survivor From : Survivor To = 8:1:1 。
。
。
3.2.1. 永久代和元数据区的区别: 元空间并不在虚拟机中,而是使用本地内存 3.2.2. 特点: 1>. 线程共享: 整个Java虚拟机只有一个堆,所有的线程都访问同一个堆。而程序计数器、Java虚拟机栈、本地方法栈都是一个线程对应一个的。 2>. 在虚拟机启动时创建 3>. 垃圾回收的主要场所。 4>. 不同的区域存放具有不同生命周期的对象。这样可以根据不同的区域使用不同的垃圾回收算法,从而更具有针对性,更高效。 5>. 堆的大小既可以固定也可以扩展,但主流的虚拟机堆的大小是可扩展的,因此当线程请求分配内存,但堆已满,且内存已满无法再扩展时,就抛出OutOfMemoryError。 3.2.3. 新生代: 新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。 新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivor space),所有的类都是在伊甸区被new出来的。 幸存区有两个:0区(Survivor 0 space)和1区(Survivor 1 space)。 年龄参数: MaxTenuringThreshold 当对象在Survivor区躲过一次GC的话,其对象年龄便会加1 默认情况下,如果对象年龄达到15岁,就会移动到老年代中 当伊甸区的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园进行垃圾回收Minor GC(又叫Young GC),将伊甸区中的剩余对象移动到幸存0区。若幸存0区也满了,移动到1区。那如果1区也满了呢?再移动到老年代。若老年代也满了,那么这个时候将产生Major GC(又叫Old GC), 进行老年代的内存清理, Major GC后老年代仍然无法保存,则产生Full GC。若执行Full GC 之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。 如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二: a.Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。 b.代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用).
分配担保 (老年代为新生代作担保): 当JVM准备为一个对象分配内存空间时,发现此时Eden+Survior中空闲的区域无法装下该对象,那么就会触发MinorGC,对该区域的废弃对象进行回收。但如果MinorGC过后只有少量对象被回收,仍然无法装下新对象,那么此时需要将Eden+Survior中的所有对象都转移到老年代中,然后再将新对象存入Eden区。这个过程就是“分配担保” 。
3.2.4. 永久代 永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。 如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。原因有二: a. 程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用; b. 大量动态反射生成的类不断被加载,最终导致Perm区被占满。 3.2.5. 对象创建过程 1>. 检查常量池中是否有即将要创建的这个对象所属的类的符号引用: 若常量池中没有这个类的符号引用,说明这个类还没有被定义!抛出ClassNotFoundException; 若常量池中有这个类的符号引用,则进行下一步工作 2>. 检查这个符号引用所代表的类是否已经被JVM加载: 若该类还没有被加载,就找该类的class文件,并加载进方法区; 若该类已经被JVM加载,则准备为对象分配内存 3>. 根据方法区中该类的信息确定该类所需的内存大小: 一个对象所需的内存大小是在这个对象所属类被定义完就能确定的!且一个类所生产的所有对象的内存大小是一样的!JVM在一个类被加载进方法区的时候就知道该类生产的每一个对象所需要的内存大小。 4>. 从堆中划分一块对应大小的内存空间给新的对象 5>. 为对象中的成员变量赋上初始值(默认初始化) 6>. 设置对象头中的信息: 对象头(哈希值、GC分代年龄、数据长度) 7>. 调用对象的构造函数进行初始化 。
栈是线程私有的,线程安全的。它的生命期跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致。 3.3.1. 栈运行原理 栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,B方法又调用了C方法,于是产生栈帧F3也被压入栈…… 依次执行完毕后,先弹出后进......F3栈帧,再弹出F2栈帧,再弹出F1栈帧。遵循“先进后出”/“后进先出”(FILO)原则。 3.3.2. 栈存储(栈帧中主要存储以下数据): 栈帧数据(Frame Data): 包括类文件、方法等等。 本地变量表/局部变量表(Local Variables): 输入参数和输出参数以及方法内的变量; 栈操作/操作数栈(Operand Stack): 记录出栈、入栈的操作; 动态链接: 在运行期间将符号引用转化为直接引用,就称为动态链接 方法出口信息: 返回值 3.3.3. 特点: 1>. 局部变量表的创建是在方法被执行的时候,随着栈帧的创建而创建。而且,局部变量表的大小在编译时期就确定下来了,在创建的时候只需分配事先规定好的大小即可。此外,在方法运行的过程中局部变量表的大小是不会发生改变的。 2>. 虚拟机栈会出现两种异常:StackOverFlowError和OutOfMemoryError。 a) StackOverFlowError: 当线程请求栈的深度超过当前虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。 b) OutOfMemoryError: 当线程请求栈时内存用完了,此时抛出OutOfMemoryError异常.
程序计数器是线程私有,线程安全的。程序计数器是一块较小的内存空间,可以把它看作当前线程正在执行的字节码的行号指示器。也就是说,程序计数器里面记录的是当前线程正在执行的那一条字节码指令的地址。 3.4.1. 程序计数器的作用: 1>. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 2>. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了 3.4.2. 特点: 1>. 是一块较小的存储空间 2>. 线程私有。每条线程都有一个程序计数器。 3>. 是唯一一个不会出现OutOfMemoryError的内存区域。 4>. 生命周期随着线程的创建而创建,随着线程的结束而死亡.
本地方法栈类似于VM栈,主要存储了本地方法调用的状态。在Sun JDK中,本地方法栈和VM栈是同一个.
执行引擎是JVM执行Java字节码的核心,执行方式主要分为解释执行、编译执行、自适应优化执行、硬件芯片执行方式。 解释执行: 有三种优化方式(1: 栈顶缓存; 2: 部分栈帧共享; 3: 执行机器指令) 编译执行: 主要利用了JIT(Just-In-Time)编译器在运行时进行编译,它会在第一次执行时编译字节码为机器码并缓存,之后就可以重复利用 自适应优化执行: 自适应优化执行的思想是程序中10%~20%的代码占据了80%~90%的执行时间,所以通过将那少部分代码编译为优化过的机器码就可以大大提升执行效率.
1>. 引用计数法: 每个对象都有一个计数器,当这个对象被一个变量或另一个对象引用一次,该计数器加一;若该引用失效则计数器减一。当计数器为0时,就认为该对象是无效对象。 2>. 可达性分析法: 所有和GC Roots直接或间接关联的对象都是有效对象,和GC Roots没有关联的对象就是无效对象.
Java中可作为根集合(GC Root)的对象有: 1). 虚拟机栈中引用的对象(本地变量表) 2). 方法区中静态属性引用的对象 3). 方法区中常量引用的对象 4). 本地方法栈中引用的对象(Native对象) Java中的四种引用: 1.强引用;2:软引用;3:弱引用;4:虚引用(幽灵/幻影引用) 引用计数法虽然简单,但存在一个严重的问题,它无法解决循环引用的问题。 因此,目前主流语言均使用可达性分析方法来判断对象是否有效.
1). 引用计数器(Java1.2之前) 2). 标记-清除算法: 采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。 3). 复制算法: 采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间用于进行对象的移动。 4). 标记-整理算法: 标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但解决了内存碎片的问题.
新生代的GC: 串行GC(Serial GC)、并行GC(ParNew GC)、并行回收GC(Parallel Scavenge GC) 旧生代的GC: 串行GC(Serial MSC)、并行GC(parallel MSC)、并发GC(CMS) Serial: 串行收集器并不是只能使用一个CPU进行收集,而是当JVM需要进行垃圾回收的时候,需要中断所有的用户线程,直到它回收结束为止,因此又号称“Stop The World” 的垃圾回收器。 ParNew: 多线程版本的Serial收集器 Parallel Scavenge: 又称为是吞吐量优先的收集器,假设程序运行了100分钟,JVM的垃圾回收占用1分钟,那么吞吐量就是99%.
G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,G1垃圾收集器没有新生代和老年代的概念了,而是将堆划分为一块块独立的Region。当要进行垃圾收集时,首先估计每个Region中的垃圾数量,每次都从垃圾回收价值最大的Region开始回收,因此可以获得最大的回收效率 产生背景: 作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推出,相信熟悉JVM的同学们都不会对它感到陌生。在JDK 9中,G1被提议设置为默认垃圾收集器(JEP 248)。 G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色: 1). G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。 2). G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间.
对JVM内存调优的主要目的是减少GC频率和Full GC的次数,过多的GC和Full GC是会占用很多的系统资源(主要是CPU),影响系统的吞吐量。特别要关注Full GC,因为它会对整个堆进行整理 导致Full GC一般由于以下几种情况: 1). 旧生代空间不足: 调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 2). Permanet Generation空间不足: 增大Perm Gen空间,避免太多静态对象,控制好新生代和旧生代的比例 3). System.gc()被显示调用: 垃圾回收不要手动触发,尽量依靠JVM自身的机制 调优手段主要是通过控制堆内存的各个部分的比例和GC策略来实现,下面来看看各部分比例不良设置会导致什么后果 1). 新生代设置过小: 一是新生代GC次数非常频繁,增大系统消耗;二是导致大对象直接进入旧生代,占据了旧生代剩余空间,诱发Full GC 2). 新生代设置过大: 一是新生代设置过大会导致旧生代过小(堆总量一定),从而诱发Full GC;二是新生代GC耗时大幅度增加,一般说来新生代占整个堆1/3比较合适 3). Survivor设置过小: 导致对象从eden直接到达旧生代,降低了在新生代的存活时间 4). Survivor设置过大: 导致eden过小,增加了GC频率 JVM提供两种较为简单的GC策略: 1). 吞吐量优先: JVM以吞吐量为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,来达到吞吐量指标。这个值可由-XX:GCTimeRatio=n来设置 2). 暂停时间优先: JVM以暂停时间为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,尽量保证每次GC造成的应用停止时间都在指定的数值范围内完成。这个值可由-XX:MaxGCPauseRatio=n来设置 JVM常见配置: 1). 堆设置 -Xmn: 新生代内存大小的最大值,包括E区和两个S区的总和 -Xms: 初始堆大小 -Xmx: 最大堆大小 -Xss: 设置每个线程的栈内存,默认1M -XX:NewSize=n: 设置年轻代大小 -XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 -XX:PermSize=n:设置持久代大小 -XX:MaxPermSize=n:设置持久代最大值 JDK1.8 -XX:MetaspaceSize=n:初始元空间大小 -XX:MaxMetaspaceSize=n:元空间最大值,默认是没有限制的 2). 收集器设置 -XX:+UseSerialGC:设置串行收集器 -XX:+UseParallelGC:设置并行收集器 -XX:+UseParNewGC: 设置年轻代为并行收集 -XX:+UseParalledlOldGC:设置并行年老代收集器 -XX:+UseConcMarkSweepGC:设置并发收集器 -XX:+UseCompressedOops: 压缩堆大小,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP 3). 并行收集器设置 -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。 -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间 -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) 4). 并发收集器设置 -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。 -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。 5). CMS相关参数 -XX:+UseConcMarkSweepGC 使用CMS内存收集 6). 垃圾回收统计信息 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:filename 。
1). jstat命令行工具监控JVM内存和垃圾回收 2). Java VisualVM及Visual GC插件 3). JConsole 。
最后此篇关于JVM原理剖析的文章就讲到这里了,如果你想了解更多关于JVM原理剖析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我的 Django 应用程序在生产过程中变得非常缓慢。可能是由于某些复杂或未索引的查询。 是否有任何类似 django 的方法来分析我的应用程序? 最佳答案 试试 Django Debug Toolb
我正在使用GDownloadUrl将客户端数据发送到服务器。这里是我使用的sode GDownloadUrl( dwnld_url, function(data) {
我一直在尝试开始分析我的 CherryPy 网络服务器,但文档缺乏关于如何设置它的详细信息。我知道我应该能够使用 cherrypy.lib.profiler 作为中间件来安装我的初始服务器。现在,我有
是否有任何 HashMap 实现公开了用于分析 Map 性能的钩子(Hook)方法(平均链长度、最佳/最差/平均访问时间、#rehashes 等)。 在 ~O(1) 访问时间方面使用 HashMap
我想知道是否有可用的技术或工具可以告诉您执行特定方法需要多少时间。 类似于数学/计算机科学中的大 O 符号,可以让您了解算法的复杂性,我想知道代码分析是否有类似的东西。 最佳答案 Profiling是
前面,我们说Ruby没有函数,只有方法.而且实际上有不止一种方法.这一节我们介绍访问控制(accesscontrols). 想想当我们在"最高层"而不是在一个类的定义里定义一个
我有一个用 Visual Basic 编写的大型应用程序6,我需要分析它。有没有类似 ANTS Profiler 的东西但对于 COM应用程序? 最佳答案 我以前用 VBWatch .我对它有很好的体
我做了两个实现来解决 Shikaku 难题。一个使用顶部、左侧、宽度和高度 (TLWH) 作为每个矩形的参数,另一个使用顶部、左侧、底部、右侧 (TLBR)。 出于某种原因,使用 TLBR 的速度要快
如果重要的话,我正在使用 Very Sleepy CS 进行分析。 IPP(Intel's Integrated Performance Primitives)在我的项目中使用,基本上是单次调用: i
我想弄明白这个排列算法是如何工作的: def perm(n, i): if i == len(n) - 1: print n else: for j i
如果 C++ 项目的物理结构有利于编译速度,您将如何获得某种客观的衡量标准?有些人说你应该避免使用模板以获得更好的编译速度,但是如果模板生成大量非冗余目标代码,即使行/秒编译速度不是很好,那也不是很糟
摘自 Robert Sedgewick 和 Kevin Wayne 算法第 4 版 在递归部分基本情况代码是 if(end start) { mid = (start+end
有没有一种简单的方法可以计算一段标准 C 代码实际执行的乘法次数?我想到的代码基本上只是做加法和乘法,主要兴趣是乘法,但也可以计算其他操作的数量。 如果这是一个选项,我想我可以四处用“multiply
我正在编写一个 Netty 应用程序。该应用程序运行在 64 位八核 linux 机器上 Netty 应用程序是一个简单的路由器,它接受请求(传入管道),从请求中读取一些元数据并将数据转发到远程服务(
我希望能得到一些帮助来弄清楚这个异常消息到底对我说了什么。我能够使用调试器将问题缩小到代码中的特定行。但是,我认为更多信息可以更好地找出代码中的实际问题。 public static List
我有一个存储在 USB 拇指驱动器上的 mysql 数据库,该数据库已不可挽回地丢失了其文件分配表。因此,我无法访问整个 ibdata1 文件。不过,我可以找到使用十六进制编辑器使用的记录页面。 所有
我正在使用 jQuery 在单击时显示图像。通过将图像源存储到像这样的变量中,这可以很好地工作.. var theSrc = $(event.target).attr('src'); 然后我可以将这张
我是 R 的新手,但不是 C 的新手。我想看看是否可以为 friend 加速一个包。通常使用 C 我会编译一个设置了 -pg 标志的应用程序,然后将 gmon.out 文件传递给 gprof。 我
在分析我的代码以找出运行缓慢的地方时,我有 3 个功能显然会永远占用,这就是非常困的说法。 这些函数是: ZwDelayExecution 20.460813 20.460813 19.
我正在分析一个应用程序并注意到 52% (195MB) 的内存被 char[] 使用,20% 被 String 使用。这是一个有很多依赖项的大型项目,我刚刚看到它,所以我有几个相关的问题可以帮助我开始
我是一名优秀的程序员,十分优秀!