iLeichun

当前位置:首页

Java内存泄漏分析与解决方案

分类:Java  来源:网络  时间:2010-9-14 23:45:52

  Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位网友分享解决这些问题的办法。

  作为Internet最流行的编程语言之一,Java现正非常流行。我们的网络应用程序就主要采用Java语言开发,大体上分为客户端、服务器和数据库三个层次。在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现java.lang.OutOfMemoryError为止。经过分析Java内存泄漏是破坏系统的主要因素。这里与大家分享我们在开发过程中遇到的Java内存泄漏的检测和处理解决过程.

  一. Java是如何管理内存

  为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。

  Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

  在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。虽然,我们有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者可能使用不同的算法管理GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。

  二. 什么是Java中的内存泄露

  导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。

  在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

  这里引用一个常看到的例子,在下面的代码中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。

  Vector v = new Vector(10);for (int i = 1; i < 100; i++)......{ Object o = new Object(); v.add(o); o = null;}//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。

  实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因。再引用另一个例子来说明Java的内存泄漏。假设有一个日志类Logger,其提供一个静态的log(String msg),任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中。

  Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)的时候,都首先将message的值写入temp中(以当前线程+当前时间为键),在退出之前再从temp中将以当前线程和当前时间为键的条目删除。注意,这里当前时间是不断变化的,所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目。这样,任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用,而无法得到回收,这种对象保持就是我们所说的Java内存泄漏。总的来说,内存管理中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。

  三. 几种典型的内存泄漏

  我们知道了在Java中确实会存在内存泄漏,那么就让我们看一看几种典型的泄漏,并找出他们发生的原因和解决方法。

  3.1 全局集合

  在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。

  通常有很多不同的解决形式,其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。

  另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。

  3.2 缓存

  缓存一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行需要比较多的资源并会多次被使用,通常做法是把常用的输入数据的操作结果进行缓存,以便在下次调用该操作时使用缓存的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果,因此需要将所使用的内存容量与检索数据的速度加以平衡。

  常用的解决途径是使用java.lang.ref.SoftReference类坚持将对象放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。

  3.3 类装载器

  Java类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构,因为类装载器不仅仅是只与"常规"对象引用有关,同时也和对象内部的引用有关。比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。

Java和ant在windows下的环境变量的配置方法

分类:Java  来源:网络  时间:2010-9-14 23:44:53

  以下设置WinXP及以上用户,右键点击我的电脑->属性->高级->环境变量,设置即可。设置完成后需重新启动命令提示符才能生效。具体如下:

  JAVA环境变量设置

  Windows下JAVA用到的环境变量主要有3个,JAVA_HOME、CLASSPATH、PATH。

  JAVA_HOME指向的是JDK的安装路径,如D:/Program Files/IBM/Java50,在这路径下你应该能够找到bin、lib等目录。

  设置方法:

  JAVA_HOME = D:/Program Files/IBM/JAVA50

  PATH环境变量原来Windows里面就有,只要保留原来的PATH的内容,并在其中加上%JAVA_HOME%/bin (%JAVA_HOME%就是是引用上一步设定好的环境变量JAVA_HOME) 。

  设置方法:

  PATH = %JAVA_HOME%/bin;%PATH%

  同样,%PATH%是引用以前你设置的PATH环境变量,你照抄以前的值就行了。

  注意的是最前面的"./;",这个是告诉JDK,搜索CLASS时先查找当前目录的CLASS文件,至于CLASSPATH后面指定tools.jar这个具体文件,是由java语言的import机制和jar机制决定的。

  设置方法:

  CLASSPATH = ./;%JAVA_HOME%/lib/tools.jar;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/jre/lib/rt.jar

  ANT环境变量设置

  Windows下ANT用到的环境变量主要有2个,ANT_HOME 、PATH。

  设置ANT_HOME指向ant的安装目录。

  设置方法:

  ANT_HOME = D:/apache_ant_1.7.0

  将%ANT_HOME%/bin; %ANT_HOME%/lib添加到环境变量的path中。

  设置方法:

  PATH = %ANT_HOME%/bin; %ANT_HOME%/lib

Java趣味编程:快速排序法

分类:Java  来源:网络  时间:2010-9-14 23:42:57

  快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧) 的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。

  java


  1 void quickSort(int[] arr, int left, int right){
  2 int p = partition(arr, left, right);
  3 quickSort(arr, left, p-1);
  4 quickSort(arr, p+1, right);
  5 }
  6 //1:
  7 int partition(int[] arr, int left, int right){
  8 int p = left/2 + right/2;
  9 while(left < right){
  10 while(left < p && arr[left] <= arr[p]){
  11 left++;
  12 }
  13 while(right > p && arr[right] >= arr[p]){
  14 right--;
  15 }
  16 swap(arr[left], arr[right]);
  17 }
  18 return p;
  19 }
  20
  21 /*2: compare首先从high位置向前搜索找到第一个小于compare值的索引,并置换(这时high索引位置上的值为compare值);然后从 low位置往后搜索找到第一个大于compare值的索引,并与high索引上的值置换(这时low索引位置上的值为compare值);重复这两步直到 low=high为止。*/
  22 int partition(int[] arr, int left, int right){
  23 compare = a[left];
  24 while(left < right){
  25 while(left=compare){
  26 right--;
  27 }
  28 swap(arr[left], arr[right]);
  29
  30 while(left 
  31 left++;
  32 }
  33 swap(arr[left], arr[right]);
  34 }
  35 return left;
  36 }

 

Java游戏开发现状分析和走向

分类:Java  来源:网络  时间:2010-9-14 23:42:33

  这篇文章是基本上是基于我的论文开题报告,所以难免要打点官腔。因为我对手机游戏并不了解,所以本文关于java手机游戏的讨论会很少,而即使对于手机游戏以外的内容,我个人所能接触到的资源也是很有限的,讨论起来不免会有所偏颇,大家批判着看吧。

  这些年由于Java技术的不断发展,Java程序的运行速度有了显着提升,同时硬件技术也在不断进步,从而使得java处理比较复杂的3d图形成为可能。 Lwjgl,Jogl,Java3D等技术的出现,使Java语言可以方便的利用现在的显卡日益强大的硬件加速能力。实际上目前已经有许多基于java的 2d和3d游戏,其中不乏大型多人在线游戏。

  Wurm online一个多人在线网络游戏(06年的时候进入了收费运行阶段,不过你还是可以申请一个免费帐号进去看看):

  [url]http://www.wurmonline.com/[/url]

  2007年javaone上jmokeyengine演示视频:

  [url]http://www.youtube.com/watch?v=ho_b18HRmGA&eurl=http%3A%2F%2Fwww%2Ejmonkeyengine%2Ecom%2F[/url]

  一个比较老的视频2005GDC上java游戏演示视频:

  [url]http://www.youtube.com/watch?v=Pc3LxBIVaBs&feature=related[/url]

  上面这些只是Java游戏的一小部分。然而,即使还有其它很多基于Java的游戏,相对于整个游戏产业来说,Java游戏社区仍然非常小,以至于其力量在手机游戏之外的领域几乎可以被忽略不计。但是实际上Java作为一种游戏编程语言和平台,这些年还是取得了长足的发展,在后applet时代走着复兴之路。

  当然,Java可以做游戏,并不意味着Java就适合做游戏,那么:

  基于java对于一个游戏来说到底意味着什么?

  1,Java是一门经过充分发展,已经十分成熟的语言。

  Java语言称霸企业级应用这么多年,事实证明了它非常适合于开发大型软件。当Java的运行效率不再是问题的时候,Jjava语言严谨、高抽象、可读性强的优势就体现出来了。这对于规模越来越庞大的游戏软件来说,有着不小的诱惑。

  2,Java作为平台,极大的方便了游戏脚本的使用

  对于游戏开发来说,由于涉及到比较复杂的情况(有时候甚至是美学问题),开发过程很多时候是一个不断修改的过程,可以方便的修改游戏内容是十分关键的。对于很多游戏引擎来说,都有自己编写的脚本解释器,用来解释一些自定义的游戏脚本语言(一般为命令式脚本,比如moveplayer(int  x,int y),playersmile()之类的东西),另一些则提供了例如lua和rubby等的脚本语言来访问游戏引擎。由于其虚拟机特性,java本身就可以用来编写游戏脚本,目前也有例如beanshell、groovy等脚本语言可以方便的无缝的和java语言进行交互,这些都极大的方便了java游戏编程。

  3,跨平台?

  跨平台,每当比较java与其他语言的时候,总是要谈到跨平台。但是在java游戏领域,write once ,run everywhere,听起来更像是一个冷笑话。这个笑话有多冷呢?(手机游戏我不熟悉,但是确实有人用“痛苦”来形容开发运行在不同手机上的 javame游戏)对于其他大多数java游戏来说,或者通过applet或者通过java web start来部署,这两种方式无一例外的都要用到浏览器。对于java web start来说,问题相对较小,程序员必须处理的问题主要是如何让非专业用户成功安装指定的jre版本的问题,虽然很麻烦,但是是可以解决的。对于applet来说,问题就变得复杂的多,很多问题只会在特定的操作系统配上特定的浏览器时才会出现,即使是同一品牌的浏览器,不同的浏览器版本也会出现不同的问题,同时这里还涉及到客户机默认是使用opengl pipeline还是directdraw pipeline的问题。而我们现在甚至还没有开始讨论当3d游戏需要使用底层图形api时的情况,无论是applet还是web start一旦在程序中使用了任何一种opengl的java绑定,客户机各不相同的显卡以及同一款显卡的不同驱动都会带来不同的问题,而这些问题是随着操作系统和浏览器的不同而不同的,操作系统数×浏览器数(web start不需要考虑浏览器)×显卡数×每款显卡平均的驱动版本数=噩梦。最后你不得不妥协于这样一个现实-----即使电脑在硬件上满足程序的要求,你也无法让你的程序运行在这些电脑中的每一台上,除非你的用户都是专业人士。

  当然,其实现状并没有听起来那么糟糕,对于不使用java的大多数游戏来说,根本就只能运行在一个平台上,许多游戏对directx或者 opengl的版本有要求,更不要提那些运行在家用游戏机上的游戏了,跨平台是整个游戏业的大问题,java只是像其他人一样没有把这个问题彻底解决掉而已,但仍然比其它技术做得要好。不过,值得一提的是,在那些不直接使用硬件加速的网页小游戏上,java确实在稳定性和方便安装上输给了flash;在家用机方面,java是0,而微软的xna(c#)可以跨xbox360和vista。

  4,网络

  Java一向强于解决网络方面的问题,在这方面,java主要有两个优势,第一是基于applet和java web start的游戏非常容易更新,不会像其他游戏那样需要用户手动下载全新版本;第二是java在网络交互的api相对比较友好,而对于大型网络游戏来说,分布式系统非常重要,这正是jcp的巨头们喜欢的东西,可能由于这是一个卖硬件的好机会,sun下了不少功夫开发了project dark star来做网络游戏服务端,由于这个工程还没有到1.0,所以现在也很难说具体好用不好用。

  5,速度

  这个没什么可讨论的,基于java的游戏肯定会比写得很好的c++程序慢,当然写一个好的c++程序所花费的庞大的薪水也是java不能比的。

  总体说完,就具体说说:

  当前在java游戏方面主要有哪些可以使用的技术

  1,Java3D

  作为sun公司较早推出的一项3d技术,是一种高度封装的api,构建在opengl和direct3d之上,可以说是最有名的java的3d技术。由于种种原因(api设计,面向的用户不明确),java3d一直没有办法流行,不久前java3d的团队宣布暂停对java3d新功能的开发,转而为JavaFX开发3d 渲染引擎。同时sun的另一个项目 project wonderland宣布将其渲染引擎由Java3d转为JMonkeyEngine。令人唏嘘的是sun的java3d再次进入尘封状态,大量基于 java3d的程序和教程都面临一个非常尴尬的境地;令人高兴的是在这一领域终于有比sun做得更好的团队出现了。引用别人对此事的评价:Java3D is dead! Long live Java "3D"!

  2, Lwjgl([url]http://new.lwjgl.org/index.php[/url])

  全名为light weight java game library。作为一个非官方的对java游戏的全面解决方案,lwjgl将游戏图形(opengl)、音效(openal)、控制输入集合为一,以成为java界的directx(不是direct3d)而努力。其在图形方面采用了opengl的java绑定,其函数与c语言版的完全相同,所以对于习惯opengl编程的程序员来说比较友好。目前有大量的java游戏和游戏引擎是基于lwjgl的,它可以说是java游戏社区最广为被使用的函数库之一。

  原文地址 http://win.chinaunix.net/bbs/archiver/tid-26482.html

Java实现的线程池、消息队列功能方法

分类:Java  来源:网络  时间:2010-9-14 23:41:03

  ThreadPoolManager类:负责管理线程池,调用轮询的线程来访问字符串缓冲区的内容,维护缓冲区,当线程池溢出时抛出的Runnable任务被加入到字符缓冲区。


 public class ThreadPoolManager
  {
  private static ThreadPoolManager tpm = new ThreadPoolManager();
  // 线程池维护线程的最少数量
  private final static int CORE_POOL_SIZE = 4;
  // 线程池维护线程的最大数量
  private final static int MAX_POOL_SIZE = 10;
  // 线程池维护线程所允许的空闲时间
  private final static int KEEP_ALIVE_TIME = 0;
  // 线程池所使用的缓冲队列大小
  private final static int WORK_QUEUE_SIZE = 10;
  // 消息缓冲队列
  Queue msgQueue = new LinkedList();
  // 访问消息缓存的调度线程
  final Runnable accessBufferThread = new Runnable()
  {
  public void run()
  {
  // 查看是否有待定请求,如果有,则创建一个新的AccessDBThread,并添加到线程池中
  if( hasMoreAcquire() )
  {
  String msg = ( String ) msgQueue.poll();
  Runnable task = new AccessDBThread( msg );
  threadPool.execute( task );
  }
  }
  };
  final RejectedExecutionHandler handler = new RejectedExecutionHandler()
  {
  public void rejectedExecution( Runnable r, ThreadPoolExecutor executor )
  {
  System.out.println(((AccessDBThread )r).getMsg()+"消息放入队列中重新等待执行");
  msgQueue.offer((( AccessDBThread ) r ).getMsg() );
  }
  };
  // 管理数据库访问的线程池
  final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
  CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
  new ArrayBlockingQueue( WORK_QUEUE_SIZE ), this.handler );
  // 调度线程池
  final ScheduledExecutorService scheduler = Executors
  .newScheduledThreadPool( 1 );
  final ScheduledFuture taskHandler = scheduler.scheduleAtFixedRate(
  accessBufferThread, 0, 1, TimeUnit.SECONDS );
  public static ThreadPoolManager newInstance()
  {
  return tpm;
  }

Hibernate一对多关系的处理

分类:Hibernate  来源:网络  时间:2010-9-14 23:40:19

  一对多关系式Hibernate中的重要知识。感觉在Hibernate学习中最重要的也是最难的问题就是配置文件了,对于Hibernate一对多关系的配置***.hbm.xml现总结如下:


  ?xml version="1.0" encoding="gbk"?
  !DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
  hibernate-mapping package="此处填写包文件" 
  class name="一对多中一的实体类的类名" table="所对应的数据库的表名" 
  id name="所对应的实体类的属性(id)" column="所对应的数据表的字段(**_id)" unsaved-value="null" 
  generator class="native"/
  /id
  property name="对应数据表中其他的字段" /property
  set name="对应实体类中的那个集合的属性" inverse="true" lazy="true" cascade="save-update" key column="对应其外键的字段 "/one-to-many class="对应一对多中多的实体类的类名"/ /set/classclass name="对应一对多中多的实体类的类名" table="相对应的数据表名" 
  id name="id" column="对应表的主键字段名" unsaved-value="null" 
  generator class="native"//idproperty name="对应数据表中的其他字段" /propertymany-to-one name="对应一对多中一的实体类的属性" column="对应实体类中的那个集合的属性" /many-to-one/class/hibernate-mapping

  Hibernate一对多关系的配置代码如下:


!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test"  class name="Person" table="Person1"  id name="pid" column="p_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update"  key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer"  id name="id" column="c_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" hibernate-mapping package="entity.test"  class name="Person" table="Person1"  id name="pid" column="p_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property set name="computers" inverse="true" lazy="true" cascade="save-update"  key column="p_id"/ one-to-many class="Computer"/ /set /class class name="Computer" table="Computer"  id name="id" column="c_id" unsaved-value="null"  generator class="native"/ /id property name="name" /property many-to-one name="person" column="p_id" /many-to-one /class /hibernate-mapping 

  整个的Hibernate技术就是围绕着配置文件去发展。

Hibernate批量更新与删除实例浅析

分类:Hibernate  来源:网络  时间:2010-9-14 23:39:48

  在我们的Java项目中,批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。批量删除虽然在Hibernate里也可以实现,但因Hibernate的实现机制是一个一个删除,在数量大的情况下很影响效率;其实Hibernate提供的JDBC接口,可以方便的进行批量的更新和删除。

  以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段:

 

  如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法会一下子加载1万个Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate执行1万条更新CUSTOMERS表的update语句:

 

  以上批量更新方式有两个缺点:

  (1)占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。

  (2)执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。

  为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存:


tx = session.beginTransaction();   
Iterator customers=session.find("from Customer c where c.age>0").iterator();   
while(customers.hasNext()){Customer customer=(Customer)customers.next();   
customer.setAge(customer.getAge()+1);   
session.flush();session.evict(customer);   
}   
tx.commit();session.close(); 

  在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使Hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。

  但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句:

  


update CUSTOMERS set AGE=AGE+1 where AGE>0;

  那么,以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句:

 

 

  以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。

  如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:

 

 

  以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:

 

 

  从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。

  Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:


 session.delete("from Customer c where c.age>0");

  如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句:


 delete from CUSTOMERS where AGE>0;

  Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中:


  select * from CUSTOMERS where AGE>0;

  接下来执行一万条delete语句,逐个删除Customer对象:

 

  由此可见,直接通过Hibernate API进行批量更新和批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用相关的存储过程,是批量更新和批量删除的最佳方式,这两种方式都有以下优点:

  (1)无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。

  (2)能在一条SQL语句中更新或删除大批量的数据。


delete from CUSTOMERS where ID=i;   
delete from CUSTOMERS where ID=j;   
……   
delete from CUSTOMERS where ID=k; 


tx = session.beginTransaction();   
Connection con=session.connection();   
String procedure = "{call batchUpdateCustomer(?) }";   
CallableStatement cstmt = con.prepareCall(procedure);cstmt.setInt(1,0); //把年龄参数设为0cstmt.executeUpdate();   
tx.commit(); 


create or replace procedure batchUpdateCustomer(p_age in number)    
    asbeginupdate CUSTOMERS    
    set AGE=AGE+1 where AGE>p_age;   
    end; 


tx = session.beginTransaction();   
Connection con=session.connection();   
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "+"where AGE>0 ");stmt.executeUpdate();   
tx.commit(); 


update CUSTOMERS set AGE=? …. where ID=i;   
update CUSTOMERS set AGE=? …. where ID=j;   
……   
update CUSTOMERS set AGE=? …. where ID=k; 


tx = session.beginTransaction();   
Iterator customers=session.find("from Customer c where c.age>0").iterator();   
while(customers.hasNext()){Customer customer=(Customer)customers.next();   
customer.setAge(customer.getAge()+1);   
}   
tx.commit();   
session.close(); 

用Java统计java代码行数

分类:Java  来源:网络  时间:2010-9-14 23:30:58

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
*
* @author LC
*
*/
public class SumJavaCode {
static long normalLines = 0; // 空行
static long commentLines = 0; // 注释行
static long whiteLines = 0; // 代码行
public static void main(String[] args) {
SumJavaCode sjc = new SumJavaCode();
//目录
File f = new File("D:MyEclipse7_1Worklog_strutssrccomase");
System.out.println(f.getName());
sjc.treeFile(f);
System.out.println("空行:" + normalLines);
System.out.println("注释行:" + commentLines);
System.out.println("代码行:" + whiteLines);
}
/**
* 查找出一个目录下所有的.java文件
*
* @param f 要查找的目录
*/
private void treeFile(File f) {
File[] childs = f.listFiles();
//int count = 0;
//int sum = 0;
for (int i = 0; i < childs.length; i++) {
// System.out.println(preStr + childs[i].getName());
if (!childs[i].isDirectory()) {
if (childs[i].getName().matches(".*//.java$")) {
System.out.println(childs[i].getName());
//count ++;
sumCode(childs[i]);
}
} else {
treeFile(childs[i]);
//sum += count;
}
}
}
/**
* 计算一个.java文件中的代码行,空行,注释行
*
* @param file
* 要计算的.java文件
*/
private void sumCode(File file) {
BufferedReader br = null;
boolean comment = false;
try {
br = new BufferedReader(new FileReader(file));
String line = "";
try {
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.matches("^[//s&&[^//n]]*$")) {
whiteLines++;
} else if (line.startsWith("/*") && !line.endsWith("*/")) {
commentLines++;
comment = true;
} else if (true == comment) {
commentLines++;
if (line.endsWith("*/")) {
comment = false;
}
} else if (line.startsWith("//")) {
commentLines++;
} else {
normalLines++;
}
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
br = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

Dreamweaver 中的自动代码修改

分类:Dreamweaver  来源:网络  时间:2010-9-14 10:53:30

您可以设置选项指示 Dreamweaver 根据您指定的条件自动清理手工编写的代码。但是,不会改写您的代码,除非启用了代码改写选项或者您执行了更改代码的操作。例如,Dreamweaver 不会更改空白或更改属性的大小写,除非您使用“应用源格式”命令。

有些代码改写选项在默认情况下处于启用状态。

通过 Dreamweaver 的 Roundtrip HTML 格式的功能,您可以在基于文本的 HTML 编辑器和 Dreamweaver 之间来回移动文档,并且对文档的原始 HTML 源代码的内容和结构只有极小的影响或没有任何影响。这些功能包括:

  • 使用第三方文本编辑器来编辑当前文档。

  • 默认情况下,Dreamweaver 不更改在其它 HTML 编辑器中创建或编辑的代码(即使该代码无效),除非启用了代码改写选项。

  • Dreamweaver 不更改它不识别的标签(包括 XML 标签),因为它不具有判断这些标签的条件。如果未被识别的标签与另一标签重叠(例如, <MyNewTag><em>text</MyNewTag></em>),Dreamweaver 将其标记为错误,但不改写代码。

  • 您可以选择将 Dreamweaver 设置为在“代码”视图中高亮显示(用黄色)无效的代码。在您选择高亮显示的部分后,属性检查器将显示有关如何纠正该错误的信息。

 

 

为什么诺基亚弃 Android 是犯下了大错

分类:默认分类  来源:网络  时间:2010-9-13 19:53:24

 

  诺基亚(Nokia)这家手持设备制造商将它的未来赌在了其新的 MeeGo 操作系统上。但如果它曾与谷歌(Google)合作的话可能会更容易获得成功。

  在如今这个由谷歌的 Android、苹果(Apple)的 iOS,以及黑莓(BlackBerry)操作系统主宰的市场中,诺基亚很容易被忽略。这个手机业巨头在 20 世纪 90 年代和 21 世纪初时凭借冲击性的手机设计扬名,如今却看着它在全球主要市场份额中节节败退,于是他们需要寻求如何(如果还有机会的话)绝地反击的解答。

  一种简单的方法是模仿另一家风云一时的技术巨头——摩托罗拉——将其业务中的软件方面转手给谷歌,而专心研制足以夺人眼球的硬件。但这个主意似乎并不能打动芬兰人。他们反而决心双管齐下,延续他历来的战略。

  在上周的一次采访中,诺基亚市场执行副总裁 Niklas Savander 辩称若由采用 Android 则会很大程度将诺基亚的“命运”交给别人掌握。诺基亚(NOKBF)无法靠谷歌平台增加其价值,于是它决不会考虑使用这个免费、高速发展的操作系统。诺基亚反而像深信不疑的占星师一样,仍然相信它必须将现在的路走下去,向着它的命运前进。这也就是说它要依赖于一个叫作 MeeGo 的基于 Linux的开源操作系统,等今年末发布类似谣言盛传的 N9 之类的智能手机时采用它。

  对于这些说法,我认为只有后者可能是值得保留的。其余的都是想以双管齐下对抗现在应用程序开发者和客户都热捧的战略。

  显然,诺基亚想要保留和 RIM(RIMM)与苹果(AAPL)一样的产业联合,同时开发硬件和软件。其合理性在于:同时拥有两者可能达到极其高的利润,而且理论上可以制造出运行得更好的机器。但前提是人们同时需要硬件和软件。你听到过有人在谈论任何令人兴奋的 MeeGo 应用程序吗?说实话,诺基亚只是在 6 月发布了其 MeeGo 的早期开发版本。但这个基于应用程序的操作系统一开始就存在明显的弱点。它比苹果和谷歌,甚至比很晚才意识到智能手机“要么应用程序,要么拜拜”的现实情况的 RIM 都落后了几年。而且,等到发布最终版本,MeeGo 很可能会和预计在 2010 年假期中发布的 Windows 7 手机针锋相对,到时候连网络操作系统设备 HP(HPQ)也会出炉。

  正如 AT&T 移动和消费市场经理和首席执行官 Ralph De La Vega 在头脑风暴技术会议(Brainstorm Tech conference)上指出的,移动操作系统的战场已经非常热闹了——而诺基亚却正在想要进入这个早已拥挤不堪的地方。

  Savander 说公司无法从 Android 中增加价值的辩解也站不住脚。有了谷歌(GOOG)的 Android 操作系统,诺基亚就得到大好机会利用一款免费、开源建立、提供包括修改操作系统的外观和底层的几乎无穷无尽的可定制性的移动操作系统。HTC 已经利用这些优点创造了其成功的 Sense 界面。其他的公司也找到了利用开源软件来加快发展的方法,其中最著名的有 IBM(IBM),它依靠利用开源软件重新规划了整个 3.73 千万美元的全球技术服务(Global Technology Services)业务。

  这里我们就要回头谈谈摩托罗拉(MOT)了,Savander 应该以之为鉴了解该做什么。在最近的两年里,摩托罗拉放弃了它自己基于 Linux的智能手机操作系统以及 Windows Mobile,选择了 Android。它留住了 Razr 铁杆用户,继续开发让消费者为之疯狂的硬件。该公司第二季度的销售额超过预期达到 54 亿美元,这都多亏了 Droid 和 Droid X 那不小的贡献。

  创建一款适合诺基亚的 Android 皮肤就能让诺基亚花更多的资源在它的强项——硬件——上,并更快地向市场推出一流产品。而且跨出 Android 这一大步,就能将诺基亚的形象从移动手机“昔日王者”转为复活的竞争者,而这正是 RIM 通过其黑莓 6 操作系统和黑莓 Torch 的发布所没能做到的。相反地,诺基亚的未来现在全靠下面这些方面:

  在一个已经由上百万的 Android、iOS 和黑莓操作系统手机趋于饱和的市场中寻求对一款崭新的、没有什么口碑的操作系统的关键性公众认可。

  运营商大力的推荐。这对于制造商来说是个特别的挑战,制造商们已经承认和国内的运营商建立关系是很有挑战性的。(如果你想看看运营商放弃某个操作系统会有什么后果,那就看看 Verizon 的 Palm Pre 好了。)

  开发者的支持。第三方开发者拥有了足够多的目标平台选择——也足够多可以担心的平台选择——是否会想将资源投入到另一款操作系统中呢?(一些开发者已经表达了对于 MeeGo 的信息平台的可行性的初步担忧。)

  不管怎么样,在接下来的几个月或几年中诺基亚有许多工作要完成,还有许多要向人们证明。但如果这家公司能选择 Android 这匹良驹——或者即使是 Windows Mobile 7 也行——它的成功就更有把握了。

  更新:在早期的草稿中,我们说摩托罗拉那 54 亿美元是利润,不过该数据其实是该制造商的销售额。

  • 60
  • |<
  • <<
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • >>
  • >|