Java的线程调度 *** 作在运行时是与平台无关的 一个多任务系统需要在任务之间实现QoS(Quality of Service)管理时 如果CPU资源的分配基于Java线程的优先级 那么它在不同平台上运行时的效果是很难预测的 本文利用协调式多任务模型 提出一个与平台无关 并且能在任务间动态分配CPU资源的方案
现在 由于计算机系统已经从人机交互逐步向机机交互转化 计算机和计算机之间的业务对于时间的要求非常高 软件系统对于业务的支持已经不仅表现为对不同业务的逻辑和数据(算法+数据结构)支持 而且还表现为对同时处理不同任务的时效性 (任务响应速度)支持 一般 任务响应的速度可以通过算法优化及并行运算分担负载等手段来提高 但是 用户业务逻辑的复杂度决定了算法优化的发挥空间 硬件规模决定了所能够承担负载的大小 我们利用Java平台的特点 借鉴协调式多任务思想 使CPU资源能够在任务间动态分配 从而为时间要求强的任务分配更多的CPU运行资源 这也可以充分利用现有硬件 为用户业务提供最大的保障
用Java解决问题
本着软件系统结构和现实系统结构一致的思想 开发复杂业务服务的程序一般按照计算机任务和现实业务对应的思路 最终形成一个大规模的多任务系统 由于其跨平台性 Java系统可以随着业务的扩大 平滑地升级到各种硬件平台上 由于 Java自身的发展及其应用场合的不断扩大 用它实现多任务系统已经成为当前的应用方向 在J EE(Java Enterprise Edition)推出以后 Sun公司已经将Java的重心放在了服务器端(Server Side)系统的构造上 由于客户/服务器模型固有的多对一的关系 服务器端程序也必然是一个多任务系统
在Java多任务应用中 动态地将CPU资源在任务间分配有很重要的意义 比如一个 Internet服务商的系统往往有多种任务同时运行 有HTTP FTP MAIL等协议的支持 也有商务 娱乐 生活 咨询等业务的服务 在白天 网站希望系统的CPU资源尽量保障网上用户的服务质量 提高电子商务等任务的响应速度晚上则希望让自己的娱乐服务和资料下载尽可能满足下班后人们的需要 另外 在新兴的网管(比如TMN Telemunication Management Neork)等应用领域中 服务程序往往需要支持成千上万个并发响应事件的被管理对象(MO Managed Object) 对于被管理对象执行的 *** 作 不同用户在不同时刻往往有不同的时间要求
方案选择
在考虑动态分配CPU资源的实施方案时 往往有以下两点要求
须充分利用现有硬件资源 在系统空闲时 让低优先级任务也能够得到系统所能给予的最快响应
当硬件资源超负荷运行时 虽然系统中有大规模 多数量的任务不能处理 但它不应受影响 而能够顺利处理那些能够被处理的 最重要的高优先级任务
多任务系统要用多线程实现的最简单方法就是将线程和任务一一对应 动态调整线程的优先级 利用线程调度来完成CPU资源在不同任务间动态分配 这种思路在以前使用本地化代码(Native Code) 充分利用特定硬件和 *** 作系统技巧的基础上是基本可行的 但在跨平台的Java环境中 这个思路对仅有小规模任务数的简单系统才可行 原因有以下两点
lishixinzhi/Article/program/Java/hx/201311/27234方案选择
在考虑动态分配CPU资源实施方案时,往往有以下两点要求:
1. 须充分利用现有硬件资源,在系统空闲时,让低优先级任务也能够得到系统所能给予最快响应。
2.当硬件资源超负荷运行时,虽然系统中有大规模、多数量任务不能处理,但它不应受影响,而能够顺利处理那些能够被处理、最重要高优先级任务。
多任务系统要用多线程实现最简单方法就是将线程和任务一一对应,动态调整线程优先级,利用线程调度来完成CPU资源在不同任务间动态分配。这种思路在以前使用本地化代码(Native Code),充分利用特定硬件和 *** 作系统技巧基础上是基本可行。但在跨平台Java环境中,这个思路对仅有小规模任务数简单系统才可行,原因有以下两点:
1. Java线程虽然在编程角度(API)是与平台无关,但它运行效果却和不同 *** 作系统平台密切相关。为了利用更多CPU资源,Java中一个线程(Thread)就对应着不同 *** 作系统下一个真实线程。因为Java虚拟机没有实现线程调度,所以这些Java线程在不同 *** 作系统调度下运行差异性也就比较明显。例如在Windows系统中,不仅线程优先级少于Java API参数规定十个优先级,而且微软明确反对程序员动态调整线程优先级。即使在 *** 作系统中有足够优先权,让线程优先级参数和真实线程优先级对应,不同 *** 作系统调度方式也会有许多不同。这最终会造成代码在不同平台上行为变得不可预测。这就很难满足复杂、大规模并发任务众多优先级需求,从而很难达到用户业务需要达到效果。
2. 由于在Java系统中,线程被包装在一个Java语言对象类—Thread中,所以为了完成Java语言对象和 *** 作系统线程对应,Java线程系统开销还是比较大(在NT 4.0中,平均每个线程大致占用30KB内存)。因此如果让Thread对象个数和成千上万任务数同比例增长,就显然是不合理。
内容摘要:本文利用协调式多任务模型,提出一个与平台无关、并且能在任务间动态分配CPU资源方案。
综上所述,根据并发多任务大规模需求和Java平台固有特点,想要利用Java Thread对象优先级调整CPU资源分配是非常困难,所以应该尽量避免让线程和任务直接对应,也尽量避免使用 *** 作系统线程优先级调度机制。
解决方案
根据以上分析,问题症结在于:多任务系统中任务在Java语言中对应以及任务间相互调度。
从本质上看,一个任务就是一系列对象方法调用序列,与JavaThread对象或者别类对象没有必然联系。在避免使用不同 *** 作系统线程调度且同时Java虚拟机又没有线程调度能力情况下,要想构造一个协调式多任务系统,让各个任务相互配合就成了最直接思路。协调式多任务系统一般有以下特点:
1. 任务由消息驱动,消息响应代码完成任务逻辑处理
2. 消息队列完成消息存储和管理,从而利用消息处理次序体现任务优先级不同
3. 任务中耗时消息响应逻辑能够主动放弃CPU资源,让别任务执行(像Windows 3.1中Yield函数、Visual Basic中DoEvents语句)。
可能出于巧合,Java语言具有构造协调式多任务系统天然条件。Java对象方法不仅是一个函数调用,它还是一个java.lang.reflect.Method类对象。而所有对象方法都可以通过Method类invoke方法调用。如果能使每个任务所对应一系列方法全部以对象形式包装成消息,放到消息队列中,然后再按照自己优先级算法将队列中消息取出,执行其Method对象invoke调用,那么一个基本协调式多任务系统就形成了。其中,任务优先级和线程优先级没有绑定关系。该系统主体调度函数可以设置成一个“死循环”,按照需要优先级算法处理消息队列。对于有多重循环、外设等待等耗时 *** 作消息响应函数,可以在响应函数内部递归调用主体调度函数,这一次调用把原来“死循环”改成在消息队列长度减少到一定程度(或者为空)后退出。退出后,函数返回,执行刚才没有完成消息响应逻辑,这样就非常自然地实现了协调式系统中任务主动放弃CPU资源要求。
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的 *** 作系统和一个java虚拟机。JAVA程序启动时JVM都会分配一个初始内存和最大内存给这个应用程序。这个初始内存和最大内存在一定程度都会影响程序的性能。比如说在应用程序用到最大内存的时候,JVM是要先去做垃圾回收的动作,释放被占用的一些内存。所以想调整Tomcat的启动时初始内存和最大内存就需要向JVM声明,一般的JAVA程序在运行都可以通过中-Xms -Xmx来调整应用程序的初始内存和最大内存:这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。
Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。有以下几种方法可以选用:
第一种方法:
Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh的前面,增加如下设置:
JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】'
需要把这个两个参数值调大。例如:
JAVA_OPTS='-Xms256m -Xmx512m'
表示初始化内存为256MB,可以使用的最大内存为512MB。
第二种方法: 环境变量中设
变量名:JAVA_OPTS
变量值:-Xms512m -Xmx512m
第三种方法:前两种方法针对的是bin目录下有catalina.bat的情况(比如直接解压的Tomcat等),但是有些安装版的Tomcat下没有catalina.bat,这个时候可以采用如下方法,当然这个方法也是最通用的方法:
打开tomcatHome//bin//tomcat5w.exe,点击Java选项卡,然后将会发现其中有这么两项:Initial memory pool和Maximum memory pool.Initial memory pool这个就是初始化设置的内存的大小。Maximum memory pool这个是最大内存的大小 设置完了就按确定然后再重启TOMCAT你就会发现tomcat中jvm可用的内存改变了
另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。如果你把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。
如果系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。如果垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的详细输出,研究 垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的 80% 作为堆大小。当增加处理器时,记得增加内存,因为分配可以并行进行,而垃圾收集不是并行的。
一个要注意的地方:建议把内存的最高值跟最低值的差值缩小,不然会浪费很多内存的, 最低值加大 ,最高值可以随便设,但是要根据实际的物理内存 ,如果内存设置太大了,比如设置了512M最大内存,但如果没有512M可用内存,Tomcat就不能启动,还有可能存在内存被系统回收,终止进程的情况。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)