-
不使用定时器时Jmeter 线程将按顺序执行各个取样器而不会中途暂停Jmeter 可能会在很短时间内发出过多请求,从而使服务器不堪重负 使用定时器时可以在取样器下添加定时器,这样定时器只会作用于当前取样器也可以在线程组下添加多个定时器,统计定时器的总和,然后作用于线程组下的所有取样器 有什么定时器
-
前言学习了RT-Thread的内核组件与线程管理,我忽然发现,线程创建时,名字可以使用RT_NULL。创建无名线程,工作正常,只是,无法删除相关代码#include <rtthread.h> #define DBG_SECTION_NAME "noname" #define DBG_LEVEL DBG_LOG #include <rtdbg.h> /* led test */ extern void led_red_on(void); extern void led_red_off(void); extern void led_grn_on(void); extern void led_grn_off(void); extern void led_blu_on(void); extern void led_blu_off(void); /* no name thread1 */ static void no_name_task1(void* param) { LOG_D("no name thread1 start!\n"); while (1) { led_red_on(); rt_thread_mdelay(2000); led_red_off(); rt_thread_mdelay(2000); } } /* no name thread2 */ static void no_name_task2(void* param) { LOG_D("no name thread2 start!\n"); while (1) { led_blu_on(); rt_thread_mdelay(3000); led_blu_off(); rt_thread_mdelay(3000); } } /* no name thread3 */ static void no_name_task3(void* param) { LOG_D("no name thread3 start!\n"); while (1) { led_grn_on(); rt_thread_mdelay(4000); led_grn_off(); rt_thread_mdelay(4000); } } void no_name_init(void) { rt_thread_t tid; tid = rt_thread_create(RT_NULL, no_name_task1, RT_NULL, 2048, 28, 50); rt_thread_startup(tid); tid = rt_thread_create(RT_NULL, no_name_task2, RT_NULL, 2048, 28, 50); rt_thread_startup(tid); tid = rt_thread_create(RT_NULL, no_name_task3, RT_NULL, 2048, 28, 50); rt_thread_startup(tid); } void no_name_del(void) { rt_thread_t tid; tid = rt_thread_find(RT_NULL); if (tid != RT_NULL) { rt_thread_delete (tid); } else { LOG_D("Not find thread!"); } } MSH_CMD_EXPORT(no_name_init, no_name_init); MSH_CMD_EXPORT(no_name_del, no_name_del);msh >no_name_init msh >[D/noname] no name thread1 start! [D/noname] no name thread2 start! [D/noname] no name thread3 start! msh >list_thread thread pri status sp stack size max used left tick error -------- --- ------- ---------- ---------- ------ ---------- --- " 28 suspend 0x0000007c 0x00000800 07% 0x0000002e 000 " 28 suspend 0x0000007c 0x00000800 07% 0x0000002e 000 " 28 suspend 0x0000007c 0x00000800 07% 0x0000002e 000 pms 28 suspend 0x0000009c 0x00000800 07% 0x0000002f 000 tshell 20 running 0x00000084 0x00001000 12% 0x00000005 000 serial 25 suspend 0x00000088 0x00000400 13% 0x0000000a 000 tidle0 31 ready 0x00000080 0x00000800 06% 0x00000016 000 timer 4 suspend 0x00000060 0x00000200 18% 0x00000009 000 msh > msh >no_name_del [D/noname] Not find thread! 解读之所能创建无名的线程,因为内核对象初始化时,并不判断name的长度之所无法删除没有名字的线程,是因为内核对象的查找,是用名字来查找的。总结需要继续研究RT-Thread内核代码、线程管理代码,正确使用RT-Thread加深基础理论知识的积累。
-
重点每个测试计划至少需要有一个线程组 线程组下不同组件的执行优先级/顺序配置元件、监听器前置处理器定时器逻辑控制器取样器后置处理器断言 取样器执行顺序在没有逻辑控制器情况下,取样器是按从上往下的顺序执行的
-
Concurrency Thread Group的介绍Concurrency Thread Group提供了用于配置多个线程计划的简化方法该线程组目的是为了保持并发水平,意味着如果并发线程不够,则在运行线程中启动额外的线程和Standard Thread Group不同,它不会预先创建所有线程,因此不会使用额外的内存对于上篇讲到的Stepping Thread Group来说,Concurrency Thread Group是个更好的选择,因为它允许线程优雅地完成其工作Concurrency Thread Group提供了更好的用户行为模拟,因为它使您可以更轻松地控制测试的时间,并创建替换线程以防线程在过程中完成 Concurrency Thread Group参数讲解Target Concurrency:目标并发(线程数)Ramp Up Time:启动时间;若设置 1 min,则目标线程在1 imn内全部启动Ramp-Up Steps Count:阶梯次数;若设置 6 ,则目标线程在 1min 内分六次阶梯加压(启动线程);每次启动的线程数 = 目标线程数 / 阶梯次数 = 60 / 6 = 10Hold Target Rate Time:持续负载运行时间;若设置 2 ,则启动完所有线程后,持续负载运行 2 min,然后再结束Time Unit:时间单位(分钟或者秒)Thread Iterations Limit:线程迭代次数限制(循环次数);默认为空,理解成永远,如果运行时间到达Ramp Up Time + Hold Target Rate Time,则停止运行线程【不建议设置该值】Log Threads Status into File:将线程状态记录到文件中(将线程启动和线程停止事件保存为日志文件);特别注意点Target Concurrency只是个期望值,实际不一定可以达到这个并发数,得看上面的配置【电脑性能、网络、内存、CPU等因素都会影响最终并发线程数】Jmeter会根据Target Concurrency的值和当前处于活动状态的线程数来判断当前并发线程数是否达到了Target Concurrency;若没有,则会不断启动线程,尽力让并发线程数达到Target Concurrency的值 Concurrency Thread Group和Stepping Thread Group的区别官方说法Stepping Thread Group不提供设置启动延迟时间,阶梯增压过渡时间,阶梯释放过渡时间,但Concurrency Thread Group提供Stepping Thread Group可以阶梯释放线程,而Concurrency Thread Group是瞬时释放(具体看下面介绍)Stepping Thread Group设置了需要启动多少个线程就会严格执行,Concurrency Thread Group会尽力启动线程达到Target Concurrency值 通俗点理解Stepping Thread Group 是手动场景:测试过程,按照设定好的步骤执行Concurrency Thread Group 是目标场景:达到某个目标运行场景,测试过程不可控,动态变化 类比 LRStepping Thread Group :设置并发用户数,持续时间等,每隔多少时间自动增加多少个用户Concurrency Thread Group:预设一个目标并发数,每隔一段时间增加一部分并发数,直到 TPS 达到目标并发数,然后持续运行一段时间 Concurrency Thread Group + Active Threads Over Time第一个关注点:阶梯增压过程看Concurrency Thread Group负载预览图每次阶梯增压都是瞬时增压的,但是实际测试结果可以看到它也是有一个过渡期,并不是瞬时增压第二个关注点:持续负载运行结束后,所有线程瞬时释放从图最后可以看到,所有线程都是瞬时释放的普通的线程组有三种状态:启动、运行、释放;而Concurrency Thread Group的线程可以理解成只有两种状态:启动、运行;因为线程都在极短的时间内就结束了 Concurrency Thread Group特殊情况上图可以看到,活跃状态的线程上下波动的很厉害大致原因受环境稳定性影响,如电脑网络、内存、CPU;活跃线程数会有波动如何解决遇到这种情况可以多调试几次,看看情况如何结论其实有波动是正常的,我们没必要纠结我们要重点注意的是TPS线程组的负载只是压力值,我们要测的是服务端的性能,需要关注的是服务端的TPS
-
前言Stepping Thread Group是第一个自定义线程组但,随着版本的迭代,已经有更好的线程组代替Stepping Thread Group了【Concurrency Thread Group】,所以说Stepping Thread Group已经是过去式了但,咱们还是要介绍下的滴 Stepping Thread Group的特性有预览图显示估计的负载可延迟启动线程组可持续增加线程负载可设置最大负载的持续运行时间 Stepping Thread Group的作用减少服务器的瞬时压力,做性能测试应该逐步增加压力,而不是瞬时加压逐步增压越平缓越好,更容易从结果看到多少压力值下,有性能瓶颈 Stepping Thread Group参数详解this group will start:表示总共要启动的线程数;若设置为 100,表示总共会加载到 100 个线程first,wait for:从运行之后多长时间开始启动线程;若设置为 0 秒,表示运行之后立即启动线程then start:初次启动多少个线程;若设置为 0 个,表示初次不启动线程next add:之后每次启动多少个线程;若设置为 10个,表示每个梯次启动 10 个线程threads every:当前运行多长时间后再次启动线程,即每一次线程启动完成之后的持续时间;若设置为 30 秒,每梯次启动完线程之后再运行 30 秒using ramp-up:启动线程的时间;若设置为 5 秒,表示每次启动线程都持续 5 秒(和基础线程组的ramp-up一样意思)then hold load for:线程全部启动完之后持续运行多长时间,如图:设置为 60 秒,表示 100 个线程全部启动完之后再持续运行 60 秒finally,stop/threads every:多长时间释放多少个线程;若设置为 5 个和 1 秒,表示持续负载结束之后每 1 秒钟释放 5 个线程 从负载预览图,读懂所有参数从第0秒开始启动线程,每 5 秒内启动10个线程并且运行30秒,以此循环,直到一共启动了 100 个线程当已启动 100 个线程后,持续负载运行60秒持续负载运行60秒后,每 1 秒释放五个线程,直到全部线程被释放【注意:线程释放过程中,线程依然在运行】 结合Active Threads Over Time运行Stepping Thread Group需要和Active Threads Over Time结合起来使用,这样能看到动态的阶梯加压效果可以看到和Stepping Thread Group负载预览图基本一致,证明加压效果是正常的
-
单例模式应该是设计模式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例模式的应用。首先我们先来看一下单例模式的定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。单例模式的要素:1.私有的静态的实例对象2.私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例)3.公有的、静态的、访问该实例对象的方法单例模式分为懒汉形和饿汉式懒汉式:应用刚启动的时候,并不创建实例,当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。(时间换空间)优点:实例在被使用的时候才被创建,可以节省系统资源,体现了延迟加载的思想。缺点:由于系统刚启动时且未被外部调用时,实例没有创建;如果一时间有多个线程同时调用LazySingleton.getLazyInstance()方法很有可能会产生多个实例。例子:123456789101112publicclassSingletonClass{ //私有构造函数,保证类不能通过new创建privateSingletonClass(){}privatestaticSingletonClassinstance=null; publicstaticSingletonClassgetInstance(){if(instance==null){ //创建本类对象 instance=newSingletonClass();}returninstance;} }饿汉式:应用刚启动的时候,不管外部有没有调用该类的实例方法,该类的实例就已经创建好了。(空间换时间。)优点:写法简单,在多线程下也能保证单例实例的唯一性,不用同步,运行效率高。缺点:在外部没有使用到该类的时候,该类的实例就创建了,若该类实例的创建比较消耗系统资源,并且外部一直没有调用该实例,那么这部分的系统资源的消耗是没有意义的。例子:123456789101112131415161718192021222324252627282930313233publicclassSingleton{//首先自己在内部定义自己的一个实例,只供内部调用privatestaticfinalSingletoninstance=newSingleton();//私有构造函数 privateSingleton(){}//提供了静态方法,外部可以直接调用publicstaticSingletongetInstance(){returninstance;}}下面模拟单例模式在多线程下会出现的问题/***懒汉式单例类*/publicclassLazySingleton{//为了易于模拟多线程下,懒汉式出现的问题,我们在创建实例的构造函数里面使当前线程暂停了50毫秒privateLazySingleton(){try{Thread.sleep(50);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("生成LazySingleton实例一次!");}privatestaticLazySingletonlazyInstance=null;publicstaticLazySingletongetLazyInstance(){if(lazyInstance==null){lazyInstance=newLazySingleton();}returnlazyInstance;}}测试代码:我们在测试代码里面新建了10个线程,让这10个线程同时调用LazySingleton.getLazyInstance()方法12345678910111213publicclassSingletonTest{publicstaticvoidmain(String[]args){ //创建十个线程调for(inti=0;i<10;i++){newThread(){@Overridepublicvoidrun(){LazySingleton.getLazyInstance();}}.start();}}}结果:生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!生成LazySingleton实例一次!可以看出单例模式懒汉式在多线程的并发下也会出现问题,分析一下:多个线程同时访问上面的懒汉式单例,现在有两个线程A和B同时访问LazySingleton.getLazyInstance()方法。假设A先得到CPU的时间切片,A执行到if(lazyInstance==null)时,由于lazyInstance之前并没有实例化,所以lazyInstance==null为true,在还没有执行实例创建的时候此时CPU将执行时间分给了线程B,线程B执行到if(lazyInstance==null)时,由于lazyInstance之前并没有实例化,所以lazyInstance==null为true,线程B继续往下执行实例的创建过程,线程B创建完实例之后,返回。此时CPU将时间切片分给线程A,线程A接着开始执行实例的创建,实例创建完之后便返回。由此看线程A和线程B分别创建了一个实例(存在2个实例了),这就导致了单例的失效。解决办法:我们可以在getLazyInstance方法上加上synchronized使其同步,但是这样一来,会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式来实现呢?我们可以考虑使用"双重检查加锁"的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。我们看看具体解决代码123456789101112131415161718192021222324publicclassLazySingleton{privateLazySingleton(){try{Thread.sleep(50);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("生成LazySingleton实例一次!");}privatestaticLazySingletonlazyInstance=null;publicstaticLazySingletongetLazyInstance(){ //先检查实例是否存在,如果不存在才进入下面的同步块if(lazyInstance==null){ //同步块,线程安全地创建实例 synchronized(LazySingleton.class){ //再次检查实例是否存在,如果不存在才真正地创建实例 if(lazyInstance==null){ lazyInstance=newLazySingleton(); } }}returnlazyInstance;}}这样我们就可以在多线程并发下安全应用单例模式中的懒汉模式。这种方法在代码上可能就不怎么美观,我们可以优雅的使用一个内部类来维护单例类的实例,下面看看代码12345678910111213publicclassGracefulSingleton{privateGracefulSingleton(){System.out.println("创建GracefulSingleton实例一次!");}//类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载privatestaticclassSingletonHoder{ //静态初始化器,由JVM来保证线程安全privatestaticGracefulSingletoninstance=newGracefulSingleton();}publicstaticGracefulSingletongetInstance(){returnSingletonHoder.instance;}}说一下我在实际开发中的场景:为了程序的高效率使用多线程并发,然而是循环调用,可能导致创建线程数过多,考虑采用线程池管理,这时候创建线程池仍然是处于循环调用中,也可能导致多个线程池,这时候就考虑使用单例模式。源代码:1234567891011121314151617181920publicclassThreadPoolFactoryUtil{privateExecutorServiceexecutorService; //在构造函数中创建线程池privateThreadPoolFactoryUtil(){//获取系统处理器个数,作为线程池数量intnThreads=Runtime.getRuntime().availableProcessors();executorService=Executors.newFixedThreadPool(nThreads);}//定义一个静态内部类,内部定义静态成员创建外部类实例privatestaticclassSingletonContainer{privatestaticThreadPoolFactoryUtilutil=newThreadPoolFactoryUtil();}//获取本类对象publicstaticThreadPoolFactoryUtilgetUtil(){returnSingletonContainer.util;}publicExecutorServicegetExecutorService(){returnexecutorService;}}涉及到一个静态内部类,我们看看静态内部类的特点:1、静态内部类无需依赖于外部类,它可以独立于外部对象而存在。2、静态内部类,多个外部类的对象可以共享同一个内部类的对象。3、使用静态内部类的好处是加强了代码的封装性以及提高了代码的可读性。4、普通内部类不能声明static的方法和变量,注意这里说的是变量,常量(也就是finalstatic修饰的属性)还是可以的,而静态内部类形似外部类,没有任何限制。可以直接被用外部类名+内部类名获得。
-
什么是jvm呢? gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。1.1.引用计数 引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。1.2.对象引用遍历 早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。下一步,gc要删除不可到达的对象。删除时,有些gc只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多gc可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。 为此,gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,只有gc运行。结果,在响应期间增减了许多混杂请求。另外,更复杂的gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作,有的则采用多线程以增加效率。2.几种垃圾回收机制2.1.标记-清除收集器 这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。2.2.标记-压缩收集器 有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。2.3.复制收集器 这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,jvm生成的新对象则放在另一半空间中。gc运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。2.4.增量收集器 增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。2.5.分代收集器 这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。jvm生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。2.6.并发收集器 并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。2.7.并行收集器 并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。3.Sun HotSpot 1.4.1 JVM堆大小的调整 Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。 下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。 下面的命令是把初始大小设置为128M: java –Xms128m –Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。 下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M: java –Xms128m –Xmx128m –XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。 下面的命令把新域的初始值和最大值设置成64m: java –Xms256m –Xmx256m –Xmn64m 永久域默认大小为4m。运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm会对堆进行一次完全的垃圾收集。 使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。下面把永久域初始值设置成32m,最大值设置成64m。 java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m 默认状态下,HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden,用于生成新的对象。另两部分称为救助空间,当Eden充满时,收集器停止应用程序,把所有可到达对象复制到当前的from救助空间,一旦当前的from救助空间充满,收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio可控制新域子空间的大小。 同NewRation一样,SurvivorRation规定某救助域与Eden空间的比值。比如,以下命令把新域设置成64m,Eden占32m,每个救助域各占16m: java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2如前所述,默认状态下HotSpot对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出Eden空间时将被收集。如果能够这样的话,并且移出Eden空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助空间反复复制。但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制,可用-XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例。如救助空间位1M,该值50表示可用500K)。该值是一个百分比,默认值是50。当较大的堆栈使用较低的sruvivorratio时,应增加该值到80至90,以更好利用救助空间。用-XX:maxtenuring threshold可控制上限。 为放置所有的复制全部发生以及希望对象从eden扩展到旧域,可以把MaxTenuring Threshold设置成0。设置完成后,实际上就不再使用救助空间了,因此应把SurvivorRatio设成最大值以最大化Eden空间,设置如下: java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio=50000 …4.BEA JRockit JVM的使用 Bea WebLogic 8.1使用的新的JVM用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是Bea新JVM所在目录。不同于HotSpot把Java字节码编译成本地码,它预先编译成类。JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数)或者WebLogic Server控制台。 Bea JRockit JVM支持4种垃圾收集器: 4.1.1.分代复制收集器 它与默认的分代收集器工作策略类似。对象在新域中分配,即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。 4.1.2.单空间并发收集器 该收集器使用完整堆,并与背景线程共同工作。尽管这种收集器可以消除中断,但是收集器需花费较长的时间寻找死对象,而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾,它会中断应用程序并关闭收集。 分代并发收集器 这种收集器在护理域使用排它复制收集器,在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁,因此它需要较少的内存,应用程序的运行效率也较高,注意,过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作,甚至采用排它性工作方式完成收集。 4.1.3.并行收集器 该收集器也停止其他进程的工作,但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断,但一般能更好的利用内存,程序效率也较高。 默认状态下,JRockit使用分代并发收集器。要改变收集器,可使用-Xgc:,对应四个收集器分别为gencopy,singlecon,gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。要设置护理域,则使用-Xns:java –jrockit –Xms512m –Xmx512m –Xgc:gencon –Xns128m…尽管JRockit支持-verbose:gc开关,但它输出的信息会因收集器的不同而异。JRockit还支持memory、load和codegen的输出。 注意 :如果 使用JRockit JVM的话还可以使用WLS自带的console(C:\bea\jrockit81sp1_141_03\bin下)来监控一些数据,如cpu,memery等。要想能构监控必须在启动服务时startWeblogic.cmd中加入-Xmanagement参数。5.如何从JVM中获取信息来进行调整 -verbose.gc开关可显示gc的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。打开-xx:+ printgcdetails开关,可以详细了解gc中的变化。打开-XX: + PrintGCTimeStamps开关,可以了解这些垃圾收集发生的时间,自jvm启动以后以秒计量。最后,通过-xx: + PrintHeapAtGC开关了解堆的更详细的信息。为了了解新域的情况,可以通过-XX:=PrintTenuringDistribution开关了解获得使用期的对象权。6.Pdm系统JVM调整 6.1.服务器:前提内存1G 单CPU 可通过如下参数进行调整:-server 启用服务器模式(如果CPU多,服务器机建议使用此项) -Xms,-Xmx一般设为同样大小。 800m -Xmn 是将NewSize与MaxNewSize设为一致。320m -XX:PerSize 64m -XX:NewSize 320m 此值设大可调大新对象区,减少Full GC次数 -XX:MaxNewSize 320m -XX:NewRato NewSize设了可不设。4 -XX: SurvivorRatio 4 -XX:userParNewGC 可用来设置并行收集 -XX:ParallelGCThreads 可用来增加并行度 4 -XXUseParallelGC 设置后可以使用并行清除收集器 -XX:UseAdaptiveSizePolicy 与上面一个联合使用效果更好,利用它可以自动优化新域大小以及救助空间比值 6.2.客户机:通过在JNLP文件中设置参数来调整客户端JVM JNLP中参数:initial-heap-size和max-heap-size 这可以在framework的RequestManager中生成JNLP文件时加入上述参数,但是这些值是要求根据客户机的硬件状态变化的(如客户机的内存大小等)。建议这两个参数值设为客户机可用内存的60%(有待测试)。为了在动态生成JNLP时以上两个参数值能够随客户机不同而不同,可靠虑获得客户机系统信息并将这些嵌到首页index.jsp中作为连接请求的参数。
-
Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限, 下面看例子:复制代码代码如下: package org.thread.demo; class MyThread extends Thread{ private String name; public MyThread(String name) { super(); this.name = name; } public void run(){ for(int i=0;i<10;i++){ System.out.println("线程开始:"+this.name+",i="+i); } } } package org.thread.demo; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("线程a"); MyThread mt2=new MyThread("线程b"); mt1.run(); mt2.run(); } } 但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动 start()方法启动线程:复制代码代码如下: package org.thread.demo; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("线程a"); MyThread mt2=new MyThread("线程b"); mt1.start(); mt2.start(); } }; 这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢? 在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface) ·Runnable接口 在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。复制代码代码如下: public interface Runnable{ public void run(); } 例子:复制代码代码如下: package org.runnable.demo; class MyThread implements Runnable{ private String name; public MyThread(String name) { this.name = name; } public void run(){ for(int i=0;i<100;i++){ System.out.println("线程开始:"+this.name+",i="+i); } } }; 但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):复制代码代码如下: package org.runnable.demo; import org.runnable.demo.MyThread; public class ThreadDemo01 { public static void main(String[] args) { MyThread mt1=new MyThread("线程a"); MyThread mt2=new MyThread("线程b"); new Thread(mt1).start(); new Thread(mt2).start(); } } · 两种实现方式的区别和联系: 在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比 继承Thread类有如下好处: ->避免点继承的局限,一个类可以继承多个接口。 ->适合于资源的共享 以卖票程序为例,通过Thread类完成:复制代码代码如下: package org.demo.dff; class MyThread extends Thread{ private int ticket=10; public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("卖票:ticket"+this.ticket--); } } } }; 下面通过三个线程对象,同时卖票:复制代码代码如下: package org.demo.dff; public class ThreadTicket { public static void main(String[] args) { MyThread mt1=new MyThread(); MyThread mt2=new MyThread(); MyThread mt3=new MyThread(); mt1.start();//每个线程都各卖了10张,共卖了30张票 mt2.start();//但实际只有10张票,每个线程都卖自己的票 mt3.start();//没有达到资源共享 } } 如果用Runnable就可以实现资源共享,下面看例子:复制代码代码如下: package org.demo.runnable; class MyThread implements Runnable{ private int ticket=10; public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("卖票:ticket"+this.ticket--); } } } } package org.demo.runnable; public class RunnableTicket { public static void main(String[] args) { MyThread mt=new MyThread(); new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一 new Thread(mt).start();//个实例化对象mt,就会出现异常 new Thread(mt).start(); } };
-
同步和死锁:【问题引出】:比如说对于买票系统,有下面的代码:复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for(int i=0;i<10;++i){if(count>0){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(count--);}}}public static void main(String[] args) {hello he=new hello();Thread h1=new Thread(he);Thread h2=new Thread(he);Thread h3=new Thread(he);h1.start();h2.start();h3.start();}private int count=5;}【运行结果】:543210-1这里出现了-1,显然这个是错的。,应该票数不能为负值。如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,其他的线程必须等到这个线程结束之后才能继续执行。【使用线程同步解决问题】采用同步的话,可以使用同步代码块和同步方法两种来完成。【同步代码块】:语法格式:synchronized(同步对象){//需要同步的代码}但是一般都把当前对象this作为同步对象。比如对于上面的买票的问题,如下:复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for(int i=0;i<10;++i){synchronized (this) {if(count>0){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(count--);}}}}public static void main(String[] args) {hello he=new hello();Thread h1=new Thread(he);Thread h2=new Thread(he);Thread h3=new Thread(he);h1.start();h2.start();h3.start();}private int count=5;}【运行结果】:(每一秒输出一个结果)54321【同步方法】也可以采用同步方法。语法格式为synchronized 方法返回类型 方法名(参数列表){// 其他代码}现在,我们采用同步方法解决上面的问题。复制代码代码如下:/*** @author Rollen-Holt* */class hello implements Runnable {public void run() {for (int i = 0; i < 10; ++i) {sale();}}public synchronized void sale() {if (count > 0) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count--);}}public static void main(String[] args) {hello he = new hello();Thread h1 = new Thread(he);Thread h2 = new Thread(he);Thread h3 = new Thread(he);h1.start();h2.start();h3.start();}private int count = 5;}【运行结果】(每秒输出一个)54321提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。此处列举经典的生产者和消费者问题。【生产者和消费者问题】先看一段有问题的代码。复制代码代码如下:class Info {public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}private String name = "Rollen";private int age = 20;}/*** 生产者* */class Producer implements Runnable{private Info info=null;Producer(Info info){this.info=info;}public void run(){boolean flag=false;for(int i=0;i<25;++i){if(flag){this.info.setName("Rollen");try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.info.setAge(20);flag=false;}else{this.info.setName("chunGe");try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.info.setAge(100);flag=true;}}}}/*** 消费者类* */class Consumer implements Runnable{private Info info=null;public Consumer(Info info){this.info=info;}public void run(){for(int i=0;i<25;++i){try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}System.out.println(this.info.getName()+"<---->"+this.info.getAge());}}}/*** 测试类* */class hello{public static void main(String[] args) {Info info=new Info();Producer pro=new Producer(info);Consumer con=new Consumer(info);new Thread(pro).start();new Thread(con).start();}}【运行结果】:Rollen<---->100chunGe<---->20chunGe<---->100Rollen<---->100chunGe<---->20Rollen<---->100Rollen<---->100Rollen<---->100chunGe<---->20chunGe<---->20chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20Rollen<---->100chunGe<---->20大家可以从结果中看到,名字和年龄并没有对于。那么如何解决呢?<!--[if !supportLists]-->1) <!--[endif]-->加入同步<!--[if !supportLists]-->2) <!--[endif]-->加入等待和唤醒先来看看加入同步会是如何。复制代码代码如下:class Info {public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public synchronized void set(String name, int age){this.name=name;try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}this.age=age;}public synchronized void get(){try{Thread.sleep(100);}catch (Exception e) {e.printStackTrace();}System.out.println(this.getName()+"<===>"+this.getAge());}private String name = "Rollen";private int age = 20;}/*** 生产者* */class Producer implements Runnable {private Info info = null;Producer(Info info) {this.info = info;}public void run() {boolean flag = false;for (int i = 0; i < 25; ++i) {if (flag) {this.info.set("Rollen", 20);flag = false;} else {this.info.set("ChunGe", 100);flag = true;}}}}/*** 消费者类* */class Consumer implements Runnable {private Info info = null;public Consumer(Info info) {this.info = info;}public void run() {for (int i = 0; i < 25; ++i) {try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}this.info.get();}}}/*** 测试类* */class hello {public static void main(String[] args) {Info info = new Info();Producer pro = new Producer(info);Consumer con = new Consumer(info);new Thread(pro).start();new Thread(con).start();}}【运行结果】:Rollen<===>20ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100Rollen<===>20ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100ChunGe<===>100从运行结果来看,错乱的问题解决了,现在是Rollen 对应20,ChunGe对于100,但是还是出现了重复读取的问题,也肯定有重复覆盖的问题。如果想解决这个问题,就需要使用Object类帮忙了、,我们可以使用其中的等待和唤醒操作。
-
我们换为Runnable接口:复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,不能资源共享* */class hello implements Runnable {public void run() {for (int i = 0; i < 7; i++) {if (count > 0) {System.out.println("count= " + count--);}}}public static void main(String[] args) {hello he=new hello();new Thread(he).start();}private int count = 5;}【运行结果】:count= 5count= 4count= 3count= 2count= 1总结一下吧:实现Runnable接口比继承Thread类所具有的优势:1):适合多个相同的程序代码的线程去处理同一个资源2):可以避免java中的单继承的限制3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。所以,本人建议大家劲量实现接口。复制代码代码如下:/*** @author Rollen-Holt* 取得线程的名称* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();new Thread(he,"A").start();new Thread(he,"B").start();new Thread(he).start();}}【运行结果】:AAABThread-0Thread-0Thread-0说明如果我们没有指定名字的话,系统自动提供名字。提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。判断线程是否启动复制代码代码如下:/*** @author Rollen-Holt 判断线程是否启动* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he);System.out.println("线程启动之前---》" + demo.isAlive());demo.start();System.out.println("线程启动之后---》" + demo.isAlive());}} 【运行结果】线程启动之前---》false线程启动之后---》trueThread-0Thread-0Thread-0主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。线程的强制执行:复制代码代码如下:/*** @author Rollen-Holt 线程的强制执行* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he,"线程");demo.start();for(int i=0;i<50;++i){if(i>10){try{demo.join(); //强制执行demo}catch (Exception e) {e.printStackTrace();}}System.out.println("main 线程执行-->"+i);}}}【运行的结果】:main 线程执行-->0main 线程执行-->1main 线程执行-->2main 线程执行-->3main 线程执行-->4main 线程执行-->5main 线程执行-->6main 线程执行-->7main 线程执行-->8main 线程执行-->9main 线程执行-->10线程线程线程main 线程执行-->11main 线程执行-->12main 线程执行-->13...线程的休眠:复制代码代码如下:/*** @author Rollen-Holt 线程的休眠* */class hello implements Runnable {public void run() {for (int i = 0; i < 3; i++) {try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.start();}}【运行结果】:(结果每隔2s输出一个)线程0线程1线程2线程的中断:复制代码代码如下:/*** @author Rollen-Holt 线程的中断* */class hello implements Runnable {public void run() {System.out.println("执行run方法");try {Thread.sleep(10000);System.out.println("线程完成休眠");} catch (Exception e) {System.out.println("休眠被打断");return; //返回到程序的调用处}System.out.println("线程正常终止");}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.start();try{Thread.sleep(2000);}catch (Exception e) {e.printStackTrace();}demo.interrupt(); //2s后中断线程}}【运行结果】:执行run方法休眠被打断在java程序中,只要前台有一个线程在运行,整个java程序进程不会小时,所以此时可以设置一个后台线程,这样即使java进程小时了,此后台线程依然能够继续运行。复制代码代码如下:/*** @author Rollen-Holt 后台线程* */class hello implements Runnable {public void run() {while (true) {System.out.println(Thread.currentThread().getName() + "在运行");}}public static void main(String[] args) {hello he = new hello();Thread demo = new Thread(he, "线程");demo.setDaemon(true);demo.start();}}虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。线程的优先级:复制代码代码如下:/*** @author Rollen-Holt 线程的优先级* */class hello implements Runnable {public void run() {for(int i=0;i<5;++i){System.out.println(Thread.currentThread().getName()+"运行"+i);}}public static void main(String[] args) {Thread h1=new Thread(new hello(),"A");Thread h2=new Thread(new hello(),"B");Thread h3=new Thread(new hello(),"C");h1.setPriority(8);h2.setPriority(2);h3.setPriority(6);h1.start();h2.start();h3.start();}}【运行结果】:A运行0A运行1A运行2A运行3A运行4B运行0C运行0C运行1C运行2C运行3C运行4B运行1B运行2B运行3B运行4。但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、另外,主线程的优先级是5.线程的礼让。在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。
-
java中的多线程在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。对于直接继承Thread的类来说,代码大致框架是:复制代码代码如下:class 类名 extends Thread{方法1;方法2;…public void run(){// other code…}属性1;属性2;…}先看一个简单的例子:复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,直接调用run方法* */class hello extends Thread {public hello() {}public hello(String name) {this.name = name;}public void run() {for (int i = 0; i < 5; i++) {System.out.println(name + "运行 " + i);}}public static void main(String[] args) {hello h1=new hello("A");hello h2=new hello("B");h1.run();h2.run();}private String name;}【运行结果】:A运行 0A运行 1A运行 2A运行 3A运行 4B运行 0B运行 1B运行 2B运行 3B运行 4我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。当我们把上面的主函数修改为如下所示的时候:复制代码代码如下:public static void main(String[] args) {hello h1=new hello("A");hello h2=new hello("B");h1.start();h2.start();}然后运行程序,输出的可能的结果如下:A运行 0B运行 0B运行 1B运行 2B运行 3B运行 4A运行 1A运行 2A运行 3A运行 4因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体。那么:为什么我们不能直接调用run()方法呢?我的理解是:线程的运行需要本地操作系统的支持。如果你查看start的源代码的时候,会发现:复制代码代码如下:public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionality added* to this method in the future may have to also be added to the VM.** A zero status value corresponds to state "NEW".*/if (threadStatus != 0 || this != me)throw new IllegalThreadStateException();group.add(this);start0();if (stopBeforeStart) {stop0(throwableFromStop);}}private native void start0();注意我用红色加粗的那一条语句,说明此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。通过实现Runnable接口:大致框架是:复制代码代码如下:class 类名 implements Runnable{方法1;方法2;…public void run(){// other code…}属性1;属性2;…}来先看一个小例子吧:复制代码代码如下:/*** @author Rollen-Holt 实现Runnable接口* */class hello implements Runnable {public hello() {}public hello(String name) {this.name = name;}public void run() {for (int i = 0; i < 5; i++) {System.out.println(name + "运行 " + i);}}public static void main(String[] args) {hello h1=new hello("线程A");Thread demo= new Thread(h1);hello h2=new hello("线程B");Thread demo1=new Thread(h2);demo.start();demo1.start();}private String name;}【可能的运行结果】:线程A运行 0线程B运行 0线程B运行 1线程B运行 2线程B运行 3线程B运行 4线程A运行 1线程A运行 2线程A运行 3线程A运行 4关于选择继承Thread还是实现Runnable接口?其实Thread也是实现Runnable接口的:复制代码代码如下:class Thread implements Runnable {//…public void run() {if (target != null) {target.run();}}}其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式,我曾经写过一个小例子呵呵,大家有兴趣的话可以看一下:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.htmlThread和Runnable的区别:如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。复制代码代码如下:/*** @author Rollen-Holt 继承Thread类,不能资源共享* */class hello extends Thread {public void run() {for (int i = 0; i < 7; i++) {if (count > 0) {System.out.println("count= " + count--);}}}public static void main(String[] args) {hello h1 = new hello();hello h2 = new hello();hello h3 = new hello();h1.start();h2.start();h3.start();}private int count = 5;}【运行结果】:count= 5count= 4count= 3count= 2count= 1count= 5count= 4count= 3count= 2count= 1count= 5count= 4count= 3count= 2count= 1大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享
-
Oracle数据中有一个概念叫“检查点”,很不容易理解。检查点不是一个点,而是一个事件。这个事件的发生只有一个作用:让数据文件变“新”,或者说数据文件离数据库的状态不那么远,反正不太容易描述清楚。检查点事件的目的是让实例恢复的时间变短(妈哟,这个更不好描述)。检查点这个事件的实际动作就是:将data buffer cache中的“脏数据”写至数据文件(数据文件或者撤销文件)。将脏数据写入数据文件,怎么写?全部写,还是只写相关的部分,按照一定算法部分写?这三种写法就对应如下的三种检查点事件。据说从9i以来,数据库存在三种检查点:完全检查点,局部检查点和增量检查点。完全检查点,这个很好理解,就是将data buffer cache(标准翻译叫数据库数据缓冲区缓存)中的所有脏数据全部写入对应的数据文件和撤销文件(也是数据文件)。所以执行完全检查点的成本很高,成本主要是指cpu使用和IO使用。因为可能存在相当数量的脏数据。局部检查点,这个局部怎么理解呢?:局部的意思就是和某种相关的操作或者对象关联的数据(当然也是脏数据)。比如将一个表截断时,那么在截断该表之前,内存中很可能存在和该对象(也就是要被截断的表)相关的脏数据,这些脏数据必须要被处理(也就是要被写入相关的数据文件),然后才能真正执行截断操作。另外,将表空间/数据文件脱机,或者置于备份模式也会触发这类对象的局部检查点。还有像删除区间时,区间相关的脏数据块写入也触发局部检查点。增量检查点,这个最简单,实施这样的操作的成本也最低,就是dbwn进程根据“最近最少使用”的原则,将冷门脏数据(就是data buffer cache中的脏数据链中的最冷端)写入对应的数据文件。本着对数据库性能最小影响的原则,所以一次增量检查点写入的数据量很小。另外,根据资料显示,在联机日志自动/手动切换时,根据不会触发任何类型的检查点事件。还有,检查点事件不是由ckpt进程发起,我觉得应该是由dbwn或者服务器进程发起,ckpt进程只是在检查点事件发生时,去更新控制文件的信息而已。增量检查点由dbwn发起(dbwn进程根据自己的机制主动或者被动地发起增量检查点),完全检查点由服务器进程发起(因为完全检查点是由人为手动触发的,那么对应的应该是服务器进程)。
-
【功能模块】资源控制。 【操作步骤&问题现象】高斯集群有 3 个CN , 设置的 max_active_statements=60 , 出现异常的时候,查询出来的会话达到了 730+ active ,后来知道应该是有排队的 (当时处理人没加入排队字段,没看出来) , 也就是说 active 的有近 180 个真正的会话, 还有近 550 个是在排队的会话请求, 当时处理的同事 kill 进程,不但有进程进入 ,只有通过 lock 用户,然后清理的方式 。 问题: 除了控制 idle in transaction , active 的 max_active_statements 参数外, 排队的个数是否可以限制 ? 排队的session是否会占用资源 ? 【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
简单介绍 Random Variable 用于生成随机数字字符串并将其存储在变量中,以备后用 相比 __Random 的优势__Random 函数也可以生成随机数字字符串,但如果要重复引用的话,需要结合用户自定义变量控件(User Defined Variables)使用 Random Variable 可以生成完随机数字字符串后存储在变量中,可以直接重复引用 随机变量 随机变量界面介绍字段介绍 最简单的栗子随机变量 随机种子栗子线程组结构树有 3 个线程 循环控制器每个线程循环 2 次,一共发送 6 个请求 随机变量只有设置了随机种子和 True 才会生效 查看结果树每次循环都会产生新的随机数字字符串,所有线程共用一个数字字符串
-
测试计划参数 用户定义的变量这里用户添加的变量,相当于全局变量,所有线程组都共用一般添加一些系统常用的配置一般不建议在测试计划上添加变量,因为不方便启用(disable)和禁用(enable)可以添加用户自定义变量组件来代替,如下图 独立运行每个线程组(例如在一个组运行结束后启动下一个)默认:不勾选,默认各线程组并行、随机执行作用:勾选后,用于控制测试计划中的多个线程组的执行顺序,保证顺序执行各线程组下图是没有勾选时,多个线程组执行的顺序,可以看到并不完全是顺序执行的 下图是勾选后,多个线程组的执行顺序,可以看到当执行完第一个线程组所有取样器才会执行第二个线程组的,按顺序执行 特别注意线程组中的取样器执行顺序:默认是从上到下执行交替控制器、随机控制器、随机顺序控制器、循环控制器可以改变取样器的执行顺序 函数测试模式勾选后,如果监听器(如:查看结果树)配置了保存到一个文件中(如下图),那么jmeter会将每次的请求结果保存到文件中在负载测试中不建议勾选,平时调试脚本情况下可以勾选 添加目录或jar包到classpath当BeanShell脚本需要调用外部的java文件或jar包时,可以把jar包路径添加到这里,然后在BeanShell中直接import进来,并调用jar包中的方法
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签