分类:Java

现象:

本地开发环境Eclipse测试导出某张表的数据到Excel,报堆溢出:java.lang.OutOfMemoryError: Java heap space。但是测试环境或者开发环境启动Tomcat的时候配置-Xmx参数后就不报错。

分析流程:

先加入-Xmx参数调大堆内存到1G,测试不报错。用jdk\bin\jvisualvm查看内存情况,发现导出此表需要占用200M以上堆内存空间。

怀疑是默认堆最大内存,也就是不加-Xmx参数时可分配的最大堆内存不足,google一番,没有任何结果,包括官方文档,没有找到关于-Xmx参数默认值的说明。StackOverflow也木有啊。到SegmentFault去问了一下,无非是启动的时候打印看一下。

网上普遍介绍一个参数,-XX:+PrintFlagsFinal可以查看启动JVM时的配置值。加入此参数,报Unrecognized VM option ‘+PrintFlagsFinal’,eclipse中或者CMD中运行都会报这个错误,不支持的参数。再经过一番谷歌,有人指出这个参数是1.6.0.21之后才有的。我看了一下IDE自带的JRE是1.6.0.16。

我本机是安装了1.6.0.43的,将本地安装的jdk加入到eclipse已安装JRE中。用43版本的jre启动tomcat,加入-XX:+PrintFlagsFinal参数,发现最大堆内存是2G左右(本机8G内存),导出也没有内存溢出,瞬间懵逼。

不能用PrintFlagsFinal看最大内存,怎么办。用javaagent参数,写个JavaAgent程序,在启动tomcat之前执行。具体方法一搜一大堆。

public class MemoryAgent {
public static void premain(String agentOps, Instrumentation inst) {
System.out.println(“=========== MemoryAgent ===========”);
Long max = Runtime.getRuntime().maxMemory();
Long total = Runtime.getRuntime().totalMemory();
Long free = Runtime.getRuntime().freeMemory();
System.out.println(“=========== MAX :”+max);
System.out.println(“=========== TOTAL:”+total);
System.out.println(“=========== FREE :”+free);
}
}

果然最大堆内存是64M。

另外还有一个方法,jvisualvm也可以看到最大堆内存的值。

总结:

1.6.0.16在没有配置-Xmx标签的时候,默认最大堆内存是64M.

1.6.0.43在没有配置-Xmx标签的时候,默认最大堆内存是物理内存的1/4.

解决办法:

1、启动参数加入-Xmx并配置较大的值

2、升级JDK到1.6最大小版本或者更高大版本

3、启动参数加入-server。但是此参数会增加应用启动时间,但是会提高运行时效率,建议开发环境启用。

 

Java

之前一篇关于Java编码的文章请见:http://www.liuzhaocn.com/?p=1166

 

可以简单的将与Java有关的字符编码分成两类

1、Java源文件及JVM编码。

2、关联系统传递给Java程序的字符编码。

谈到编码,基本上我们指的是字符编码,即人类可阅读的文字在计算机中的存储与显示,其他类型编码我不懂,就不讨论了。

 

先说第一种情况。Java源文件及JVM中,字符是怎么表示的。又分两种:

1.1Java文件保存编码。

1.2JVM虚拟机运行编码。

 

1.1

我们用eclipse或者notepad++或者vim写了一个.java源文件,将其保存的时候,可以选择ANSI,GBK,UTF-8等。

实际在硬盘存储的文件编码格式,是根据用户的选择,具有多样性。

 

 

1.2

Java编译器在对源文件编译前,首先会源文件转换为Unicode编码,然后再进行编译。

比如:javac Demo.java

默认采用操作系统的编码解码Demo.java,比如,我们将Demo.java按照UTF-8保存成文件,操作系统默认编码是GBK,则编译的时候就会报错。

这时候需要指定保存的.java文件的编码格式。用:javac -encoding UTF-8 Demo.java

编译之后的.class文件中的字符,都是Unicode编码(具体来说是UTF-16BE)。

 

2、

先说一个很基础的类:java.lang.String类。String类型在JVM中存储的是Unicode编码(UTF-16BE)。同理char类型也是(16位,2字节)。

错误的理解:

以前,我总以为String类是有不同的 编码格式的,比如这个String是UTF-8的,那个String是GBK的。

比如:

byte[] bytes=…;

String newStr = new String(bytes, “GB2312”);

以前总模糊的使用,总以为是将string设置为GB2312,或者意味着将字符串由GB2312转换成了源文件的保存格式(比如UTF-8)。

正确的理解:

给定的输入源bytes的编码格式是GB2312,用GB2312解码bytes,并转换成Unicode编码(具体说是UTF-16BE)。JDK文档里说的清楚:

通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。新 String 的长度是字符集的函数,因此可能不等于 byte 数组的长度。当给定 byte 在给定字符集中无效的情况下,抛出UnsupportedEncodingException异常。

跟Java程序关联的其他系统,向Java程序发送字符的时候,就有可能出现问题。

常见的关联情况有:前台表单填写、文件读取、数据库读取、SOCKET通信、其他关联等。

基本上都是IO操作,最终传过来的信息以字节数组byte[]的方式需要Java程序处理。以从数据库中读取文本为例。

ResultSet rs;
bytep[] bytes = rs.getBytes();
String str = new String(bytes, “gb2312”);

首先需要知道文本在数据库中存放的格式,在这里是GB2312,这样才能正确的将文本读取到str对象中。

有时候我们会碰到这种写法来处理乱码的问题:

str = new String(str.getBytes(“iso8859-1”), “gb2312”);

这是因为str之前用错误的iso8859-1进行了解码,用iso8859-1编码还原成原来的byte[],再用正确的gb2312进行解码。

再说一下char类型,char类型在Java中16位存储,2个字节,与UTF-16位数一致,所以char[]与String的效果是一样的。

Java

先说编码(encode)

所谓的汉字编码,就是将一个个汉字,翻译成一个个字节(byte),进一步讲,是将一个字符串(String、char),转换成字节数组(byte[])。字节数组其实在各种编程语言中,用的地方还挺多,主要用在I/O方面。我们常说的二进制数据,在Java中,基本上可以等同于字节数组。所以,不论是序列化、文件操作、网络传输二进制流、输入输出二进制流,都会用到字节数组。

再通俗一点讲,汉字编码就是将一个汉字字符翻译成机器能识别的二进制数据。(非汉字同理)

主流的编码方式如下:

  • ASCII 码

学过计算机的人都知道 ASCII 码,总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。

  • ISO-8859-1

128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。

  • GB2312

它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。

  • GBK

全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。

  • GB18030

全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与 GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。

  • UTF-16

说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。Unicode 是 Java 和 XML 的基础,下面详细介绍 Unicode 在计算机中的存储形式。

UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。(只能说绝大多数情况下是这样,后面有说明)

  • UTF-8

UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。

UTF-8 有以下编码规则:

  1. 如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 – 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
  2. 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
  3. 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节

 

要了解Unicode就要了解UCS-2/UCS-4,还要了解BOM(Byte Order Mark)和BE/LE(Big Endian/Little Endian)。详细内容可读(http://blog.charlee.li/unicode-intro/)我这里只说一下结论吧:Unicode和UCS-2/UCS-4只是规定了字符用那些字节表示。UTF-8/UTF-16以及BOM才决定了字符在内存中真正的存储方式,真正的用什么样的字节存储,当然真正存储的时候,是需要根据UCS-2或UCS-4计算出的。

严格来说:UTF-16和UTF-8都是变长的,UTF-32才是定长的,UTF-16并不总是两个字节表示一个字符,UTF-16还可以表示UCS-4标准的字符,那个时候占用4字节,但是这一点基本上在开发中被忽略了,基本也可以被忽略。UTF-32之所以是4字节定长,是因为它完全用来表示UCS-4。

小插曲:四个“龍”字以田字形布局组成的一个汉字大家都没见过吧,但是确实在Unicode中有这个字,他用UTF-16存储就是4个字节。

我们常用的汉字,UTF-8是三字节,常用的英文字符,UTF-8是一字节。我们常用的汉字和英文字符,UTF-16都是双字节。所以UTF-8不一定比UTF-16省空间和流量,UTF-8比UTF-16更适合于网络传输的原因是,当丢失单个字节时,不会引起连锁的解码错误,而UTF-16丢失一个字节后,丢失位之后的所有字节解码都会出错。但是UTF-16占用空间更少,易于处理,所以UTF-16一般作为内存存储的编码方式。

 

再讲解码(decode)

解码是编码的反操作。如果说编码是将地球文字翻译成太阳文字,解码就是将太阳文字翻译成地球文字。

实际上,解码是将字节数组(byte[])按照源编码格式,翻译成汉字,或者翻译成另外一种编码格式的过程。

 

1、在编辑器中,如果你正在写一个.java文件,你点了保存或者Ctrl+S,这个内存中的java文件就要落地到磁盘上,这时候,系统必须要以一种编码格式对这个.java文件中的所有字符进行编码,才能翻译成二进制保存到磁盘上。

咱们编程都是英文编的,一般用任何编码方式都没什么问题。但如果你有一个变量String s=”一段中文”,这四个中文字符的存在,对文件编码格式提出了要求,可能会引起乱码。

默认情况下,保存是以操作系统的默认编码保存,一般中文操作系统是GBK。

(像notepad,可以保存成UTF-8,但是一定要保存成无BOM格式。)

理论上,java源文件,可以保存成任何编码格式,当然,如果有中文,这种格式必须支持中文,比如保存成GB2312\GBK\UTF-8\UTF-16都没有问题。

2、编译的时候。

用javac编译,是将.java文件的内容取出来,编译成.class文件。所以编译的时候,编译器得知道我之前.java文件是用那种编码方式进行的编码,如果不知道,取(解码)的时候,可就乱套了。我们可以运行一下javac -help命令,其中有一个参数-encoding就是干这件事的,官方解释是:指定源文件使用的字符编码。

如果不指定,则默认使用操作系统编码,一般中文操作系统是GBK。现在我们一般编程的时候,都会指定源码为UTF-8格式,用javac直接编译就会报错,错误提示一般是“编码GBK不可映射字符”或者“未结束的字符串面值”。那为什么我们一般很少遇到这种情况的,因为eclipse中,编译时的编码格式与文件格式一致,所以不会出现问题。这就是IDE为我们做的工作。

按照指定的源字符编码对二进制数据进行解码,这里解码后,不是用来输出,是马上用Unicode编码进行编码。Java编译的字节码文件,也就是.class文件中,所有的字符都是Unicode编码(实际是UTF-8)。这样,所有字节码文件就有了统一的,可支持全球语言的文件。

3、内存中

JVM加载.class文件的时候,就以Unicode编码方式加载就好了,汉字在内存里也是unicode方式存储的。这里实际是用UTF-16,估计是因为Java中的char数据类型都是双字节的吧。

实际上,char数据类型,在JVM中的标准就是UTF-16(无BOM)。String类型的实际存储是UTF-16BE。

4、使用时

无论我们是读取磁盘上的文件,还是读取网络传过来的数据流,我们必须知道他们正确的格式。并给我们的程序指定具体的解码类型。比如我有一个GB2312保存的本地文件,我用JAVA程序去读取里面的内容,起码我就得知道我应该用GB2312去解码,然后JVM会将解码后的字符串,转换成Unicode(UTF-16)。

当我们不知道源文件或源字符编码格式时,最好不要手动解码再编码,很有可能这个过程再也不可逆了。乱码了再也退不回的情况,我们在保存文件的时候经常遇到。(撤销操作另外,这里指再保存回原来格式的文本文件,乱码依然存在或者更严重)

5、JavaWeb开发时

说了这么多基础的知识,真到JavaWeb开发的时候,用到的可能性不大,除非是真的解决不了的编码问题,可以思考原理。

JavaWeb开发中一般需要注意的地方有这么几个:

  • 所有的Java、JSP、XML等源文件都用UTF-8格式保存。
  • XML文件头行设置<?xml version=”1.0″ encoding=”UTF-8″>
  • JSP设置编码格式<%@page contentType=”text\html; charset=UTF-8″%>
  • HTML中加入<meta HTTP-equiv=”Content-Type” content=”text/html; charset=UTF-8″/>
  • 用框架的话设置框架的编码方式为UTF-8
  • 获取表单参数之前调用request.setCharacterEncoding(“UTF-8”)
  • 设置中间件(如Tomcat)Connector的URIEncoding=”UTF-8″

一般设置前四项都不会有问题。

参考:

http://lukejin.iteye.com/blog/586088

http://www.zhihu.com/question/30945431/answer/50046808?group_id=590118722086309888

http://www.zhihu.com/question/23374078

http://blog.csdn.net/scyatcs/article/details/31356823

http://cnn237111.blog.51cto.com/2359144/1080628

http://blog.sina.com.cn/s/blog_63597c6901012oyf.html

http://stackoverflow.com/questions/2164804/from-compilation-to-runtime-how-does-java-string-encoding-really-work

https://zh.wikipedia.org/wiki/UTF-16

https://zh.wikipedia.org/wiki/UTF-8

http://blog.charlee.li/unicode-intro/

Java

一般情况下,我们访问一个Action,返回一个success,展示一个JSP页面。用的方法是:

<result name=”success”>/WEB-INF/pages/login.jsp</result>

这里result标签的type属性默认是dispatcher。正如官网所说:

If a type attribute is not specified, the framework will use the default dispatcher type, which forwards to another web resource. If the resource is a JavaServer Page, then the container will render it, using its JSP engine.

其实<result>标签有很多配置项可配置,如果我们想实现某个功能,或者解决某个问题,很有可能struts2已经想到了。

我有一个需求:一个Action当返回结果是error时,跳转到另一个Action(1、改变URL。2、为了SiteMesh对两个Action有不同的装饰需求),并且还传一个message进去。按照网友一般的办法,这样是极好的,并且没有乱码的:

<result name=”error” type=”redirect-action”>

<paramname=”actionName”>login.action</param>

<paramname=”message”>${message}</param>

</result>

但是不知道为啥,我的message接受过来,始终是乱码。后来经过一番查找和实验,发现Tomcat默认编码方式不是我的UTF-8,是ISO-8859-1。这样修改一下:

<Connector port=”8080″ protocol=”HTTP/1.1″
maxThreads=”150″ connectionTimeout=”20000″
redirectPort=”8443″ URIEncoding=”UTF-8″/>

问题解决。

当然,常规的避免乱码的方式都得有。网上一大堆,就不介绍了。

推荐:

https://struts.apache.org/docs/result-types.html

http://www.cnblogs.com/justbeginning/archive/2013/06/02/3113843.html

Java

用尽各种办法,最管用的还是下面这一种。

优点:

1、404、500都能正确跳转。包括找不到Action、找不到ActionMethod、throw Exception、Throwable。

2、500可输出错误信息。

3、与sitemesh整合没有问题。

4、可替换Struts2和Tomcat的错误页面。做到真正自定义。

配置:

Web.xml

<error-page>

<error-code>404</error-code>

<location>/error404.jsp</location>

</error-page>

<error-page>

<exception-type>java.lang.Throwable</exception-type>

<location>/error.jsp</location>

</error-page>

 

404页面可为静态的页面,没有错误信息,可自定义。

500页面如下:

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%>

<%@ page isErrorPage=”true” import=”java.io.*”%>

<%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>

<%@ taglib uri=”http://java.sun.com/jsp/jstl/fmt” prefix=”fmt” %>

<%@ taglib prefix=”s” uri=”/struts-tags”%>

Message:

<%=exception.getMessage()%>

StackTrace:

<%

StringWriter stringWriter = new StringWriter();

PrintWriter printWriter = new PrintWriter(stringWriter);

exception.printStackTrace(printWriter);

out.println(stringWriter);

printWriter.close();

stringWriter.close();

%>

以上页面都可以自己美化。

———————–

自己还试过的方法有:

1、struts2的声明式错误处理。

2、通过default-action-ref的作用来检测找不到的action,从而跳转到404页面。

以上两种方法都不好。第一种方式比较繁琐,而且有些错误捕获不到。第二种只能用于action,其他资源不能用。

<package name=”default” extends=”struts-default”>

<default-action-refname=”notFound”/>

 

<global-results>

<resultname=”error500″>/WEB-INF/pages/error500.jsp</result>

</global-results>

<global-exception-mappings>

<exception-mappingresult=”error500″exception=”java.lang.Exception”></exception-mapping>

</global-exception-mappings>

<actionname=”notFound”>

<result name=”success” type=”redirect”>

<param name=”location”>error404.jsp</param>

</result>

</action>

</package>

 

PS:多次事实证明,要解决技术问题,度娘还是不给力。

推荐几个网址:

http://www.avajava.com/tutorials/lessons/how-do-i-create-a-jsp-error-page-to-handle-exceptions.html?page=1

http://www.avajava.com/tutorials/lessons/how-do-i-create-a-jsp-error-page-to-handle-particular-error-codes.html?page=1

https://struts.apache.org/docs/home.html

Java

转载自IBM:http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0609_xuechao/

 

引言

内存泄漏是比较常见的一种应用程序性能问题,一旦发生,则系统的可用内存和性能持续下降;最终将导致内存不足(OutOfMemory),系统彻底宕掉,不能响应任何请求,其危害相当严重。同时,Java堆(Heap)中大量的对象以及对象间之复杂关系,导致内存泄漏问题的探测和分析均比较困难,采用相应的辅助工具是很必要的。

WebSphere应用服务器提供了系列针对内存问题的探测和分析诊断工具,这些工具可以帮助用户进行内存问题的及时探测,保证系统在发生OOM之前,用户可以在无须进行复杂分析的条件下,预知在其部署的应用中是否存在内存泄漏的问题。如果确有内存泄漏现象发生,WebSphere还提供了相应的工具,可以帮助用户进行分析诊断,从而找到内存泄漏的真正原因。


1. 内存泄漏探测和诊断步骤

实践中,我们可以采用以下的步骤来处理内存泄漏的问题:

(1) 首先,在WebSphere中我们启用实时探测内存泄漏工具, WebSphere性能诊断顾问会对内存泄漏提前发出警告信息。

(2) 启用WebSphere自带的Tivoli性能查看器监视系统的JVM使用状况,确定内存泄漏是否正在发生。

(3) 根据需要,生成详细内存回收日志,使用PMAT工具分析并确定泄漏的时间,周期等。

(4) 生成单个或者多个Heapdump文件,选用MDD4J进行分析诊断,找到内存泄漏的真正原因。

(5) 提交开发部门进行代码修复,然后重新部署到WebSphere应用服务器。

接下来的部分,我们针对每个环节的配置和工具使用进行阐述。


2.WebSphere应用服务器中内存泄漏的探测工具

2.1 性能诊断顾问介绍

性能诊断顾问(Performance and Diagnostic Advisor),在WebSphere应用服务器6.0.2版本之前称为运行时性能顾问(Runtime Performance Advisor)。该工具可以周期性的检查WebSphere的设置,并给出调整的推荐值。自WebSphere应用服务器6.0.2版本开始,该工具实现了一种轻量级的内存探测机制,可以非常容易的帮助用户探测是否在系统中存在内存泄漏问题,并提前通过日志和管理控制台进行通知。这样就给用户以足够的时间采取必要的措施防止系统宕掉,同时可以收集或生成相关的文件以进行离线的分析,来查找泄漏的根本原因。

2.2配置

可以在WebSphere应用服务器的管理控制台中启用性能诊断顾问

(1) 访问管理控制台 ->服务器-> 应用程序服务器。

(2) 选择所要配置的服务器。

(3) 在性能区域,选择性能和诊断顾问程序配置。

(4) 如图所示,有两个Tab, 运行时和配置。区别在于,运行时里面的内容无须重启服务器就可以生效,但下次重启服务器的时候,这些配置也会丢失。配置Tab里面的内容只有在服务器重启后才生效,而且配置的内容也会一直存在,除非再次登陆并去掉所选项。

(5)在其他属性区域,点击性能和诊断建议配置,确保内存泄漏规则处于运行状态(绿色箭头)。

2.3 查看泄漏警告信息

WebSphere性能诊断顾问输出信息可以显示在WebSphere的管理控制台,并记录在WebSphere应用服务器的SystemOut.log日志文件里面。

(1) SystemOut.log日志

 [8/31/06 13:21:43:545 CST] 00000010 TraceResponse W   TUNE9001W:  Heap
 utilization patterns indicate that you may have a memory leak 
Additional explanatory data follows.
Data values for free memory between 8/31/06 1:20 PM and 8/31/06 1:21 PM were
consistently below minimum required percentage.

(2) 管理控制台

-登陆管理控制台->故障诊断 ->运行时消息 ->点击运行时警告


3. Java 虚拟机概要分析和详细垃圾回收

进一步检测是否有内存泄漏的发生,以及泄漏发生的时间,周期和速度,我们可以启用Java虚拟机中的详细垃圾回收,然后分析相应的日志。 WebSphere应用服务器6.1使用了Java SDK5.0, 在Window, Linux, AIX, i5/OS,z/Linux 和z/OS上使用了IBM的JAVA虚拟机, 在Solaris和HP-UX上使用Sun的JVM。Java 虚拟机概要分析工具接口(Java Virtual Machine Tool Interface,JVMTI)支持从运行应用程序服务器的 Java 虚拟机(JVM)收集信息(如,关于垃圾回收的数据、对象利用和线程状态)并且支持更全面的性能分析。一旦启用了 JVMTI,可以使用 PMI 定制选项来启用所选统计信息以收集特定数据。

3.1启用 Java 虚拟机概要分析和详细垃圾回收配置步骤

配置步骤:

1. 在控制台导航树中单击服务器 > 应用程序服务器

2. 单击选择所需应用程序服务器。

3. 在”服务器基础结构”下,单击 Java 和进程管理-> 进程定义。

4. 在”其他属性”下,单击 Java 虚拟机。

5. 选中配置Tab的详细垃圾回收选项。

6. 在通用 JVM 参数字段中输入 -agentlib:pmiJvmtiProfiler。

注: WebSphere6.1中,JVM概要分析接口改为Java Virtual Machine Tool Interface (JVMTI)。之前版本是JVMPI。如果需要JVMPI的时候,也可以此处输入-XrunpmiJvmpiProfiler。另外,启用JVMTI接口对性能影响较大,尽量避免在生产环境中使用。

7.点击应用或者确定。

8. 单击保存按钮。

9.重启WebSphere应用服务器。

3.2 GC数据分析工具PMAT

在WebSphere应用服务器的日志目录下,native_stderr.log文件就是我们需要的内存回收分析文件。我们推荐使用IBM Pattern Modeling and Analysis Tool for Java Garbage Collector 工具,简称PMAT。 PMAT工具解析JAVA SDK的详细内存回收(GC)日志,并提供统计信息,图表,分析并推荐Java堆配置。PMAT提供了丰富的图形界面来显示Java堆的使用状况,从而更轻松地判断是否有内存问题发生。该工具可以从IBM的alphaWorks网站下载,只有英文版。

我们可以把GC文件从服务器上下载到PMAT所在机器,然后根据WebSphere的平台选择打开相应的GC文件进行分析。下面是一个GC日志片断,手动分析是比较费劲,而且需要深入了解JVM相关知识。

<af type="tenured" id="6" timestamp="Tue Sep 05 12:31:59 2006"
intervalms="18633.354">
  <minimum requested_bytes="8208" />
  <time exclusiveaccessms="6.240" />
  <tenured freebytes="12098304" totalbytes="268435456" percent="4" >
    <soa freebytes="51952" totalbytes="255013888" percent="0" />
    <loa freebytes="12046352" totalbytes="13421568" percent="89" />
  </tenured>
  <gc type="global" id="6" totalid="6" intervalms="18699.043">
    <classloadersunloaded count="0" timetakenms="1.598" />
    <refs_cleared soft="233" weak="856" phantom="65" />
    <finalization objectsqueued="455" />
    <timesms mark="165.502" sweep="10.989" compact="0.000" total="178.668"
	/>
    <tenured freebytes="169843272" totalbytes="268435456" percent="63" >
      <soa freebytes="157796920" totalbytes="255013888" percent="61" />
      <loa freebytes="12046352" totalbytes="13421568" percent="89" />
    </tenured>
  </gc>
  <tenured freebytes="169834952" totalbytes="268435456" percent="63" >
    <soa freebytes="157788600" totalbytes="255013888" percent="61" />
    <loa freebytes="12046352" totalbytes="13421568" percent="89" />
  </tenured>
  <time totalms="191.943" />
</af>

PMAT在分析GC日志后,给出一个总结。下图为例,我们可以看出GC对系统性能的影响,以及完成的垃圾回收次数等,并且我们可以看出工具给出的推荐(Recommendations)显示系统的Java堆使用情况是持续增加的。

进一步,我们可以查看GC的详情,点击Analysis菜单,然后选择Graph View All,我们就可以根据需要选择所要查看的曲线。如图所示,红色曲线代表已使用内存,蓝色曲线代表每次垃圾回收后可用的内存。已使用内存逐渐增加,可用内存的持续降低表明系统可能存在内存泄漏。


4. TPV监视JVM的状况

另外一种方法是借助TPV和PMI来实时监视JVM,分析性能曲线来判断是否有内存泄漏的状况发生。 WebSphere性能监控基础结构(PMI)和Java虚拟机概要分析工具接口(JVMTI)可以帮助我们收集系统的性能状况数据,使用Tivoli性能查看器(TPV)以图形的方式显示这些数据(性能计数器),可以进一步证实是否系统正在发生内存泄漏。

4.1 PMI与TPV

PMI 提供WebSphere运行时和应用程序资源行为的一组全面的数据,。例如,PMI 提供数据库连接池大小、servlet 响应时间、 Enterprise JavaBeans(EJB)方法响应时间、Java 虚拟机(JVM)垃圾回收时间以及 CPU 使用量等等。使用 PMI 数据,可以识别并修正应用程序服务器中的性能瓶颈, 还可使用 PMI 数据来监控应用程序服务器的运行状况。PMI 数据可以由 Tivoli Performance Viewer(TPV)、其他 Tivoli 工具、您自己的应用程序或第三方工具来监控和分析。TPV 是随 WebSphere Application Server 一起提供的 PMI 数据图形查看器。

Tivoli Performance Viewer(TPV)使得我们可以通过查看图表或表格,从而解读WebSphere的性能监控基础结构(PMI)数据。

4.2 PMI的配置方法

默认情况下,PMI已经开启,级别是默认(Default)。配置步骤:

1. 在控制台导航树中单击监视&调整-> 性能监视基础结构(PMI)。

2. 选择所要配置的服务器名字。

3.单击配置选项卡,这里可以根据监控内容的需要,来选择PMI的任一种统计信息集(无,基本,扩展,全部,定制)。我们这里选择”定制”。

注:如果在配置选项卡中,则当重新启动服务器时应用设置。如果在运行时选项卡中,则立即应用设置。

4.点击定制 -> 在定制监视级别的树中,选择配置选项卡,然后点开JVM运行时,可以根据需要,启用或禁用相应的计数器。

5.保存并重启WebSphere服务器。

4.3 TPV的使用方法

实时查看 TPV 性能模块的步骤:

(1) 在控制台导航树中,单击监控和调整 -> 性能查看器 -> 当前活动 -> 服务器名字)-> 性能模块。

(2) 选中要查看的每个性能模块,例如JVM运行时。

(3) 单击查看模块按钮。 在页面的右侧会显示所选性能数据的图形或切换成表格。 注:每个模块有与其关联的多个计数器。这些计数器会显示在数据图形或表格下面的表中。您可以通过选择或取消选择计数器旁的复选框,将计数器添加到图表或表中,或从中除去。

TPV显示的已使用内存的图形理想情况下应该是锯齿状,图形中每个坡(下降)对应着一次内存的垃圾回收(Garbage collection),如下图已使用内存的曲线,显示的是没有发生内存泄漏的状况。

如果测试过程中出现如下情况,则有可能发生了内存泄漏:

-每次垃圾回收后的已使用内存的数值骤增。

-TPV对应的已使用内存图形更接近于阶梯(staircase),或者锯齿形状严重不规则。

-也可以查看分配的对象数与释放的对象数之差值,如果这个数值越来越大,则有内存泄漏(如果需要查看对象数,需要启用JVMTI接口并在PMI中启用相应的JVM计数器)。

上图,红色曲线代表已使用的内存,从整体趋势,我们可以看出已使用内存一直在增长。 TPV可以帮助发现内存泄漏,为了得到最优结果,我们可以重复试验,而且每次可以增加测试的时间,例如测试1000,3000或5000个页面请求。


5. 生成Heap dump文件

WAS6.1中,在使用IBM JDK的平台上,可以直接使用以下的方法,随时生成所需的heapdump文件。如果在性能诊断顾问程序配置里面选中了”启用自动堆转储收集,则可以自动在WebSphere profile所在的路径下(例如/opt/IBM/WebSphere/WAS6.1/profiles/AppSrv01)生成heapdump文件,备用户进行分析。

在使用IBM SDK的平台上,例如AIX, Linux和Windows,在启用了性能诊断顾问工具后,如果探测到有内存泄漏发生,WebSphere会自动生成两个heapdump文件,供后续分析使用。

我们在任何时候,可以随时手动生成所需的heap dump文件。在WAS6.1 profile的bin目录下,首先运行wsadmin 脚本客户端,然后可以调用generateHeapDump操作来完成。

关键步骤:

1. 找到JVM对象名字。

<wsadmin> set objectName 
WebSphere:type=JVM,process=<WebSphere服务器名字>,node=<节点名字>,*]

2. 对JVM MBean调用generateHeapDump操作。

<wsadmin> $AdminControl invoke $objectName generateHeapDump

例如:

[[email protected] bin]# pwd
/opt/IBM/WebSphere/WAS6.1/profiles/AppSrv01/bin
[[email protected] bin]# ./wsadmin.sh -username root -password demo4you
WASX7209I: Connected to process "server1" on node csspvmNode02 using SOAP
connector;  The type of process is: UnManagedProcess
WASX8011W: AdminTask object is not available.
WASX7029I: For help, enter: "$Help help"
wsadmin>set objectName [$AdminControl queryNames
WebSphere:type=JVM,process=server1, node=csspvmNode02,*]
WebSphere:name=JVM,process=server1,platform=proxy,node=csspvmNode02,
j2eeType=JVM,J2EEServer=server1,
version=6.1.0.0,type=JVM,mbeanIdentifier=JVM,cell=csspvmNode02Cell,spec=1.0
wsadmin>$AdminControl invoke $ objectName generateHeapDump
/opt/IBM/WebSphere/WAS6.1/profiles/AppSrv01/./heapdump.20060904.075650.3576.phd
wsadmin>quit

理想情况下,在探测到问题时,尽快生成一个初始的heap dump,然后密切监控内存使用情况,等到泄漏了足够的内存的时候,再生成另外一个heap dump,这样可以对比分析以更准确地找到泄漏的原因。

注: 生成HeapDump文件的过程是比较耗资源的,所以请只在必须的时候做这样的操作。


6内存泄漏的分析诊断工具-MDD4J

一旦确定了系统中有内存泄漏,并且为此生成了heap dump。接下来,我们可以把这些文件从WebSphere应用服务器转移到离线的分析工具所在的机器,进行离线分析诊断。

6.1 工具介绍

MDD4J(Memory Dump Diagnostic for Java)是一个内存泄漏分析工具,用于对运行 WebSphere Application Server 的虚拟机(JVM)所生成的常用内存转储(堆转储)格式进行分析。进行内存转储(Memory dump)分析的目的,是为了确定 Java 堆中真正导致内存泄漏的类和包(classes and packages),这样可以缩小内存泄漏的范围并找到真正的原因,此分析还确定应用程序 Java 堆占用量的主要组成部分以及它们之间的拥有关系。

此工具支持下列格式的内存转储格式有:

-IBM 的PHD格式(heapdump.phd)

-IBM 文本堆转储(heapdump.txt)

-HPROF 堆转储格式(hprof.txt,主要针对Solaris和HP-UX平台)

-SVC 转储(dump.bin,IBM z-Series上的WebSphere)

该工具提供了两种分析机制:单转储分析以及对两个转储进行的比较分析。

单转储分析最常用于在发生 OutOfMemoryException 时自动触发的内存转储。此类分析查找可疑的数据结构,能够相对快速地提供可疑泄漏对象的分析结果。

比较分析用于对运行内存泄漏应用程序期间(即可用 Java 堆内存流失时)获取的两个内存转储进行分析。在运行泄漏应用程序的早期触发的内存转储被称为基线内存转储,发生泄漏的应用程序运行一段时间(以允许泄漏程度加大)后触发的内存转储被称为主内存转储。在发生了内存泄漏的情况下,主内存转储可能包含大量对象,而这些对象占用的 Java 堆空间量会比基线内存转储大很多。

为了获得更好的分析结果,建议使主内存转储的触发点与基线内存转储的触发点在时间上拉开一定距离,从而使总耗用堆大小在两个触发点之间大幅增长。

MDD4J的分析结果显示是基于Web界面的,具有下列特征:

– 列示分析结果、堆内容、大小和增长幅度的总结

– 列示可疑的数据结构、数据类型和包,它们是造成堆使用量增加(对于比较分析)和堆大小较大(对于单转储分析)的主要原因。

– 拥有关系上下文视图显示了占用量主要组成部分之间的关系,以及一组汇总的主要占用量组成部分所包含的重要数据类型。

– 在堆转储内容的交互式树形视图中,浏览功能能够显示堆中任何对象的所有进入引用(在树中只显示一个引用,其余引用单独显示)和外出引用,而子对象按到达大小排序。

– 导航功能使您能够从可疑对象列表转到所有关系上下文,以及从内容视图转到浏览视图。

– 提供了内存转储中所有对象和数据类型的表视图,视图中具有过滤器和经过排序的列。

6.2工具的使用

WebSphere 应用服务器v6.1的附带光盘里面有IBM Support Assistant工具的安装文件,运行相应的安装文件,MDD4J作为插件同时被安装了。

另外,也可以从IBM 技术支持站点http://www-306.ibm.com/software/support/isa/ 下载Support Assistant工具,然后选择更新程序,独立安装MDD4J插件。

启动步骤:

(1) 程序->IBM Support Assitant ->IBM Support Assistant v3

(2) 在Support Assistant窗口中,选择工具 -> 选择WebSphere版本号。

点击MDD4J的链接,就可以开启MDD4J工具。在该界面中,我们可以提交单个heap dump文件进行单转储分析,或同时提交两个文件进行比较分析。也可以从内存转储分析结果的下拉选项中选择以前的分析结果,从而查看以前的分析内容。

查看分析进度

单击”上载并分析”按钮后,MDD4J开始分析heap dump文件。在分析执行过程中,登录页面将自动刷新,以反映当前正在执行的分析步骤以及整体分析进度。如果该页面由于某种原因而不刷新,您可以单击”刷新”按钮以了解当前分析状态。如果您希望停止分析,可以单击”停止”按钮,这将在当前正在执行的模块完成后终止分析。

在提交了heap dump文件,MDD4J显示分析状态。

查看分析结果

分析完成后,Mdd4J页面将重定向到”分析结果”页面。”分析结果”页面包含 4 个选项卡:

“分析总结”选项卡:显示分析结果总结,并列示下一组用于查看分析结果的步骤。

“可疑对象”选项卡:它显示四类可疑对象,即对增长幅度影响最大的数据结构、到达大小显著流失的数据结构、有大量实例的对象类型以及有大量对象实例的 Java 包。

“察看上下文和内容”选项卡:显示主内存转储中 Java 堆占用量的主要组成部分的拥有关系上下文图,以及图中所选节点的内容。

“浏览”选项卡:根据对对象引用图执行的深度优先遍历,用树形视图显示主内存转储的所有内容。

其他内容,请参照MDD4J工具附带的Help文档,该帮助文档有详细的使用说明,在此不再赘述。


7.小结

IBM提供了一系列的工具辅助用户进行内存问题的监控和分析,在合适的阶段选择合理的工具可以帮助我们轻松搞定内存泄漏。这里介绍的工具都是WebSphere附带或者免费的,IBM Tivoli工具还提供了更强大的监控和诊断功能,例如ITCAM (IBM Tivoli Composite Application Management),可以根据实际情况选用。

Java

1.1 什么是CTG
1.1.1 概述
CICS系统是IBM大型机上的重要的交易处理系统,并且由于大型机的特殊性,导致CICS的已有应用程序对分布式平台开放不够,难以重用以往的应用程序。因此,CICS与分布式系统的交互,即外界如何访问CICS传统应用,成为一个重要的课题。
而CTG(CICS Transaction gateway,以下简称为CTG),CICS交易网关,是分布式平台连接到CICS的首选工具,它具有高性能,高安全性,高可扩展性等特性。
CTG与CICS的连接属于直接相连的方式,几乎不用修改CICS的已有应用,即可被外界访问。Web Services 同样可以实现与CICS的连接,但是相比于Web Services,CTG有以下优势:
(1)没有复杂的标准,技术相对成熟。
(2)利用已有的应用程序接口,很少需要对CICS程序进行修改。
本文中实例所使用的CICS Transaction Gateway软件为V7.2 for Windows版本。

1.1.2 CTG结构图

图像
 相关概念介绍:
1. Gateway Daemon (网关守护程序):网关守护程序用于监听远端客户程序的请求,并且可以管理线程和网络连接,以达到优化的目的。网关守护程序中的一个重要组件是协议处理器(Protocol Handler),用于处理远端应用程序的请求,可以支持TCP/IP和SSL协议。
2. Client Daemon (客户机守护程序):客户机守护程序是开放平台CTG的重要组成部分,它提供一个本地接口,客户应用程序就通过这个接口连接到CICS,支持C,C++,.Net,COBOL等多种语言的应用程序
3. IPIC driver (IPIC 驱动器):IPIC驱动器是CTG中的一个独立组件,作用类似于客户机守护程序,专用于IPIC方式连接到CICS。CICS在3.2版本中引入了IPIC(IP Intercommunication)的概念,用于CICS region之间的互连,这种连接是基于TCP/IP协议的,具体的IPIC概念请参考其他相关章节。从CTG V7.1版本以后,加入了对IPIC的支持
4. CTGCFG tool(CTG configuration tool,CTG配置工具):CTG产品自带的图形化配置工具,用于设置ctg.ini配置文件中的属性,包括网关守护程序和客户机守护程序等的相关参数。配置客户端守护程序界面,如下图所示:
图像其中重要的几个参数描述如下:
(1) 服务器名称:一个8位长的字符串,定义一个独立于协议的服务器名称,客户端应用程序发到CICS的请求都要发到这个服务器。
(2) 协议:指定CTG所支持的各种网络协议,本章重点介绍TCP/IP协议。
(3) 主机名或IP地址:CICS所在的主机地址,可以指定主机名或IP地址。
(4) 端口:CICS端定义的TCPIPService所指定的端口,是客户端请求发送到CICS的接入点。

配置客户端守护程序界面,如下图所示:
图像选中“启用协议处理程序”就能使图示的网关守护程序生效。
其中重要的几个参数描述如下:
(1)绑定地址:如果在此处指定某IP或主机名,就可以将协议处理程序绑定到上面指定的值;如果需要将协议处理程序绑定到任意地址,可以把此处留空不填。
(2)端口:安装CTG的服务器对外开放的CTG端口,默认为2006。

1.1.3 CTG编程接口
由于分布式系统架构的多样性,CTG支持一下三种连接方式:

1.    External Call Interface (ECI) 外部访问接口
ECI是简单的RPC式访问接口,用来调用(LINK) CICS中的应用程序,可以使用COMMAREA或channel作为数据接口,用于客户应用程序和CICS之间的数据交换。
CICS将此种类型的访问看成是一种分布式程序调用(DPL : distributed program link )。客户端发来的请求由一个镜像交易CPMI进行处理,然后进一步调用到目标应用程序
图像2.    External Presentation Interface (EPI) 外部表现接口
EPI被用来调用基于3270终端的交易。使用这种接口时,CICS上面会自动安装一个虚拟的终端,此终端受CTG的控制,并且CICS将客户端发来的请求运行在这个虚拟终端上。这种接口只支持SNA协议

图像
 3.    External Security Interface (ESI) 外部安全接口
ESI是用来验证并且修改CICS外部安全管理器(例如RACF)所保存的安全信息,并且由于CICS支持方面的原因,目前只能通过SNA协议进行连接。
图像1.1.4 CTG支持的网络协议
TCP/IP:配置最简单的方式。
SNA:必须和Communication Server搭配使用。
IPIC:CICS在3.2版本中引入了IPIC(IP Intercommunication)的概念,用于CICS region之间的互连,这种连接是基于TCP/IP协议的,具体的IPIC概念请参考其他相关章节。从CTG V7.1版本以后,加入了对IPIC的支持
Namedpipe:命名管道,只支持Windows版本的CTG。
EXCI:只支持Z/OS版本的CTG。

1.1.5 CTG的一些注意事项
1.CTG不支持从CICS端主动发出的访问请求。这里有个问题比较容易引起误解,就是CTG发送请求到CICS,CICS的返回信息可以正常返回给CTG,这种不是CICS主动发起的请求。
2.CTG不会做数据的转换,这个工作需要在应用程序或CICS中处理。

1.2 两种模式
CTG的有两种运行模式,这两种模式是根据CTG以及客户应用的拓扑结构来区分的。具体是哪种模式取决于客户应用程序的部署位置,分别为本地模式和远程模式。

1.本地模式(Local Mode,两层结构)
图像在本地模式下,客户应用程序与安装CTG的server为同一台机器。在客户应用程序对CICS发送请求的时候,不需要经过网关守护程序,而是直接发送到CICS端,因此,整个体系架构可以看作两层的结构。这种模式下,根本无需启动网关守护程序,对CICS的请求直接发送到客户端守护程序
在客户应用程序中的ECI请求中仍需要指定URL参数为local,即ConnectionURL = “local://” 和PortNumber = 0。

2.    远程模式(Remote Mode,三层结构)
图像在远程模式下,客户应用程序与安装CTG的server为不同的机器,甚至不同的系统平台。在这种模式下,必须要配置并且启动网关守护程序,网关守护程序介于客户应用程序和CICS之间,整体构成三层的结构。
在客户应用程序的ECI请求中需要指定URL参数为CTG所在机器的IP地址,即ConnectionURL = “XXX.XXX.XXX.XXX” 和PortNumber = XXX。

从另外一个角度对两种模式的诠释,见下图:
图像图像 1.3 CTG连接到CICS实例
1.配置CICS region
1. Set the SIT parameter TCPIP=YES.
2. Install the following:
–    CICS-supplied transient data queue CIEO, in group DFHDCTG
–    Transaction CIEP in group DFHIPECI
–    Program DFHIEP in group DFHIPECI
3. Add a TCP/IP listener to CICS. Use the following CEDA command to define a TCPIPSERVICE in a group:
–    CEDA DEF TCPIPSERVICE(service-name) GROUP(group-name)
4. Use the following command to install the TCPIPSERVICE definition:
–    CEDA INS TCPIPSERVICE(service-name) GROUP(group-name)

Attribute Description
POrtnumber The port on which the TCP/IP service listens.
PRotocol The protocol of the service is ECI.
TRansaction The transaction that CICS runs to handle incoming ECI requests. Set it to CIEP.
Backlog The number of TCP/IP requests that are queued before TCP/IP starts to reject incoming requests.
Ipaddress The IP address (in dotted decimal form) on which the TCPIPSERVICE listens. For configurations with more than one IP stack, specify ANY to make the TCPIPSERVICE listen on all addresses.
SOcketclose Whether CICS should wait before closing the socket after issuing a receive for incoming data on that socket. NO is recommended for ECI connections, to ensure that the connection from the Client daemon always remains open.
ATtachsec Specifies the level of attach-time security required for TCP/IP connections.

2.配置CTG
图像
3.测试连接

1.4 应用程序开发
CTG提供多种编程接口,包括:
(1) J2EE Connector Architecture(JCA) JAVA API,即J2EE连接器架构JAVA应用程序接口,这是推荐的编程接口。
(2)基础的JAVA,C,COBOL,COM等编程接口。

1.    Java 应用程序开发
图像图像2.    JCA 应用程序开发

JCA(J2EE Connector Architecture) defines a standard architecture for connecting the Java 2 Platform Enterprise Edition (J2EE) platform to heterogeneous Enterprise Information Systems (EIS). Examples of an EIS include transaction processing systems (such as the CICS TS) and Enterprise Resource Planning systems (such as SAP).
A resource adapter is a middle-tier between a Java application and an EIS, which enables the Java application to connect to the EIS. A resource adapter plugs into application servers supporting the JCA.
JNDI: The Java Naming and Directory Interface (JNDI) is part of the Java platform, providing applications based on Java technology with a unified interface to multiple naming and directory services.
J2C Connection Factory: The connection factory provides connections to the Enterprise Information System (EIS). It operates the connection management of JCA.
Common Client Interface (CCI) : The CCI defines a common API for interacting with resource adapters. It is independent of a specific EIS. A Java application interfaces with the resource adapter using this API.
System contracts: A set of system-level contracts between an application server and an EIS. These extend the application server to provide the following features: (Connection management, Transaction management, Security management). These system contracts are transparent to the application developer. That is, they do not implement these services themselves.
Resource adapter deployment and packaging: A resource adapter provider includes a set of Java interfaces/classes as part of the resource adapter implementation. These Java interfaces/classes are packaged together with a deployment descriptor to create a Resource Adapter Archive (represented by a file with an extension of rar). This Resource Adapter Archive is used to deploy the resource adapter into the application server.
图像        1. Look up a ConnectionFactory for the ECI resource adapter.
2. Create a Connection object using this ConnectionFactory. A Connection is a handle to the underlying network connection to the EIS. Specific connection properties, such as a user name and password, can be passed using an ECIConnectionSpec object.
3. Create an Interaction from the Connection. Specific interaction properties such as the transaction identifier can be passed using an ECIInteractionSpec object. The call to the EIS is initiated by invoking the execute() method on the interaction, passing data as input and output records.
4. After the required interactions have been processed, the interaction and connection should be closed.

1.5 应用举例

1.5.1 简单Java应用
public class CTGdemo {
… Java code …

// Program name
String strProgram = “EC01”;
// Port number
int iPort = 2006;
/*
* Use the extended constructor to set the parameters on the
* ECIRequest object
*/
eciRequestObject =
new ECIRequest(ECIRequest.ECI_SYNC,      //ECI call type
                    strChosenServer,          //CICS server
                    null,                     //CICS userid
                    null,                     //CICS password
                    strProgram,               //CICS program to be run
                    null,                     //CICS transid to be run
                    abytCommarea,             //Byte array containing the
                                              // COMMAREA
                    iCommareaSize,            //COMMAREA length
                    ECIRequest.ECI_NO_EXTEND, //ECI extend mode
                    0);                       //ECI LUW token

… Java code …
}

… COBOL code …
* Initialize commarea
MOVE SPACES TO DFHCOMMAREA.
* Invoke api to get system time
EXEC CICS
ASKTIME ABSTIME(WS-RAWTIME)
END-EXEC.
* Format
EXEC CICS
FORMATTIME ABSTIME(WS-RAWTIME)
                     DDMMYY(LK-DATE-OUT)
                     DATESEP(‘/’)
                     TIME(LK-TIME-OUT)
                     TIMESEP(‘:’)
END-EXEC.

… COBOL code …
*end of program

1.5.2 JCA应用
Lab3 implements a simple internet banking.
Users can query/save/withdraw their money from the web application.
图像 

 

 

 1. Web client
(1)Operation Page s
Operation Types: Query,Save,Withdraw
(2)Return Page
Result: Name, Gender, Balance

2. DATA FORMAT — COMMAREA
(1)SNNNNNNNNNNXXXXXXXXXX
(2)S= 0,1,2(0-Query, 1-Save, 2-Withdraw)
(3) NNNNNNNNNN — UserName
XXXXXXXXXX   —  Money
struct CTGINPUT{
char signal;
char username??(10??);
char money??(10??);
char success;
char name??(12??);
char gender??(12??);
char balance??(12??);
};
图像图像

3. CICS Program – VSAM1
(1)three operations
(Query,Save,Withdraw)
(2) Operate the VSAM files

4. CICS DATA – VSAM files
(1)VSAM file1–USERINFO
(Username:10), (Account:6)
(Name:12), (Gender:6)
(2)VSAM file2–ACCOUNT
(Account:6), (Balance:10)

 图像
原文地址:
https://www.ibm.com/developerworks/mydeveloperworks/blogs/cicschina/entry/cics_transaction_gateway69?lang=zh

Java

Struts1是非线程安全的,因为Struts1 的 Action 是单例模式,所以开发者要实现数据的同步。我们在Struts1的Action中使用的基本都是局部变量,而“局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量”。在Struts 1中,所有的变量都是定义在Action中我们要执行的方法里的(Action中的execute方法或DispatchAction中指定要执行的方法),我们用于封装客户端请求参数的ActionForm,也是作为一个参数传入,也属于局部变量,因此,线程安全问题也不是很突出。

Struts2是线程安全的。Struts 2框架在处理每一个用户请求的时候,都建立一个单独的线程进行处理。Struts 2 的 Action 对象为每一个请求产生一个实例,因此,虽然在Action中定义了很多全局变量,所以说是线程安全的。

Spring整合Struts2后,由Spring托管Action对象,而Spring默认对于对象的管理都是单例的(Singleton)这样又会产生线程安全问题。但是有人说Spring用了Threadlocal避免了Spring的线程安全问题。为了解决这个疑问,本人做了测试。

奈何测试的代码和结果都在办公本上,不能联网。先贴出测试结果吧:

测试结果:Spring与Struts2整合存在线程安全问题。Threadlocal猜测用在了DAO的管理方面。

但是Action中的属性如果开发人员用Threadlocal包装,则Action可以用单例模式。

如果Action中的属性没有用Threadlocal包装的话,还是设置为prototype把。

理解ThreadLocal

http://blog.csdn.net/qjyong/article/details/2158097

学习Spring必学的Java基础知识(6)—-ThreadLocal

http://www.iteye.com/topic/1123824

Java

是不是看到过很多的简单的Demo代码,都用foo和bar来做函数或者变量名称?

例如:

// C++ code

#include <iostream>
using namespace std;

int main()
{
   char foo[] = "Hello,";
   char bar[] = "World!";
   cout << foo << " " << bar << endl;

   return 0;
}

虽然知道这肯定是某种约定俗成的写法,也隐约觉得是从国外传过来的,但是终究好奇这种写法是怎么来的,为什么不用A/B/C,One/Two/Three呢。去互联网上搜索了一番,像我一样对这个小事情怀着这样好奇心的人是有的,他们早就提出了自己的疑问。这个习惯的产生和发展估计比较复杂,所以如果要找一个标准答案,恐怕不是那么容易。

foobar,计算机技术中的一种常见的替名词
在计算机程序设计与计算机技术的相关文档中,术语foobar是一个常见的无名氏化名,常被作为“伪变量”使用。

foo,bar的起源真是没法说了,众说纷纭啊。总起来说,正是foo,bar和foobar及FUBAR的广泛使用,使得能在不同的领域看到这几个词。

维基百科云:

最早出现在正式文档中式1930年。是一个国际标准:http://tools.ietf.org/html/rfc3092

这个文档说,foor,bar分别是Metasyntactic variable的第一个和第二个变量。另外依次还有

(bar, baz, qux, quux, corge, grault, garply, waldo, fred, plugh, xyzzy, thud)

foo,bar现在也被经常用作没有意义的占位符,也就是Metasyntactic variable

为我们其实可以理解为中文中的甲乙丙丁。

 

维基百科中文参考:http://zh.wikipedia.org/wiki/Foobar

英文略更详细的参考:http://en.wikipedia.org/wiki/Foobar

http://en.wikipedia.org/wiki/Metasyntactic_variable

http://www.catb.org/~esr/jargon/html/F/foobar.html

http://blog.csdn.net/sunyonggao/article/details/1633123

http://programmers.stackexchange.com/questions/69788/what-is-the-history-of-the-use-of-foo-and-bar-in-source-code-examples

Java 技术杂谈 生活随笔

Table 

Table用来定义entity主表的name,catalog,schema等属性元数据属性说明:

  • name: 表名
  • catalog: 对应关系数据库中的catalog
  • schema:对应关系数据库中的schema
  • UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列
  @Entity
    @Table(name="CUST")
    public class Customer { ... }

SecondaryTable 

一个entity class可以映射到多表,SecondaryTable用来定义单个从表的名字,主键名字等属性元数据属性说明:

  • name: 表名
  • catalog: 对应关系数据库中的catalog
  • schema:对应关系数据库中的schema
  • pkJoin: 定义一个PrimaryKeyJoinColumn数组,指定从表的主键列
  • UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列

下面的代码说明Customer类映射到两个表,主表名是CUSTOMER,从表名是CUST_DETAIL,从表的主键列和主表的主键列类型相同,列名为CUST_ID

  @Entity
    @Table(name="CUSTOMER")
	@SecondaryTable(name="CUST_DETAIL",[email protected](name="CUST_ID"))
	public class Customer { ... }

SecondaryTables 

当一个entity class映射到一个主表和多个从表时,用SecondaryTables来定义各个从表的属性元数据属性说明:

  • value: 定义一个SecondaryTable数组,指定每个从表的属性
	@Table(name = "CUSTOMER")
	@SecondaryTables( value = {
	@SecondaryTable(name = "CUST_NAME", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }),
	@SecondaryTable(name = "CUST_ADDRESS", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }) })
	public class Customer {}

UniqueConstraint 

UniqueConstraint定义在Table或SecondaryTable元数据里,用来指定建表时需要建唯一约束的列元数据属性说明:

  • columnNames:定义一个字符串数组,指定要建唯一约束的列名
		@Entity
		@Table(name="EMPLOYEE",
			uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
		)
		public class Employee { ... }

Column 

Column元数据定义了映射到数据库的列的所有属性:列名,是否唯一,是否允许为空,是否允许更新等元数据属性说明:

  • name:列名
  • unique: 是否唯一
  • nullable: 是否允许为空
  • insertable: 是否允许插入
  • updatable: 是否允许更新
  • columnDefinition: 定义建表时创建此列的DDL
  • secondaryTable: 从表名如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字
    public class Person {
	@Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
	private String name;

	@Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL", secondaryTable="PER_PHOTO")
	private byte[] picture;

JoinColumn 

如果在entity class的field上定义了关系(one2one或one2many等),我们通过JoinColumn来定义关系的属性JoinColumn的大部分属性和Column类似元数据属性说明:

  • name:列名
  • referencedColumnName:该列指向列的列名(建表时该列作为外键列指向关系另一端的指定列)
  • unique: 是否唯一
  • nullable: 是否允许为空
  • insertable: 是否允许插入
  • updatable: 是否允许更新
  • columnDefinition: 定义建表时创建此列的DDL
  • secondaryTable: 从表名如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。

下面的代码说明Custom和Order是一对一关系在Order对应的映射表建一个名为CUST_ID的列,该列作为外键指向Custom对应表中名为ID的列

	public class Custom {

    @OneToOne
	@JoinColumn(
	name="CUST_ID", referencedColumnName="ID", unique=true, nullable=true, updatable=true)
	public Order getOrder() {
		return order;
	}

JoinColumns 

如果在entity class的field上定义了关系(one2one或one2many等),并且关系存在多个JoinColumn,用JoinColumns定义多个JoinColumn的属性元数据属性说明:

  • value: 定义JoinColumn数组,指定每个JoinColumn的属性

下面的代码说明Custom和Order是一对一关系在Order对应的映射表建两列,一列名为CUST_ID,该列作为外键指向Custom对应表中名为ID的列,另一列名为CUST_NAME,该列作为外键指向Custom对应表中名为NAME的列

	public class Custom {
    @OneToOne
	@JoinColumns({
    @JoinColumn(name="CUST_ID", referencedColumnName="ID"),
    @JoinColumn(name="CUST_NAME", referencedColumnName="NAME")
	})
	public Order getOrder() {
		return order;
	}

声明当前field为映射表中的主键列id值的获取方式有五种:TABLE, SEQUENCE, IDENTITY, AUTO, NONE。Oracle和DB2支持SEQUENCE,SQL Server和Sybase支持IDENTITY,mysql支持AUTO所有的数据库都可以指定为AUTO,我们会根据不同数据库做转换。NONE (默认)需要用户自己指定Id的值元数据属性说明:

  • generate():主键值的获取类型
  • generator():TableGenerator的名字(当generate=GeneratorType.TABLE才需要指定该属性)

下面的代码声明Task的主键列id是自动增长的(Oracle和DB2从默认的SEQUENCE取值,SQL Server和Sybase该列建成IDENTITY,mysql该列建成auto increment)

    @Entity
    @Table(name = "OTASK")
    public class Task {
	  @Id(generate = GeneratorType.AUTO)
	  public Integer getId() {
		  return id;
	  }
    }

IdClass 

当entity class使用复合主键时,需要定义一个类作为id classid class必须符合以下要求:类必须声明为public,并提供一个声明为public的空构造函数。必须实现Serializable接,覆写 equals()和hashCode()方法。entity class的所有id field在id class都要定义,且类型一样元数据属性说明:

  • value: id class的类名

public class EmployeePK implements java.io.Serializable{ String empName; Integer empAge; public EmployeePK(){} public boolean equals(Object obj){ ……} public int hashCode(){……} } @IdClass(value=com.acme.EmployeePK.class) @Entity(access=FIELD) public class Employee { @Id String empName; @Id Integer empAge; }

MapKey 

在一对多,多对多关系中,我们可以用Map来保存集合对象默认用主键值做key,如果使用复合主键,则用id class的实例做key,如果指定了name属性,就用指定的field的值做key元数据属性说明:

  • name: 用来做key的field名字

下面的代码说明Person和Book之间是一对多关系Person的books字段是Map类型,用Book的isbn字段的值作为Map的key。

  @Table(name = "PERSON")
    public class Person {

	@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
	@MapKey(name = "isbn")
	private Map books = new HashMap();
    }

OrderBy 

在一对多,多对多关系中,有时我们希望从数据库加载出来的集合对象是按一定方式排序的,这可以通过OrderBy来实现,默认是按对象的主键升序排列元数据属性说明:

  • value: 字符串类型,指定排序方式格式为”fieldName1 [ASC|DESC],fieldName2 [ASC|DESC],……”,排序类型可以不指定,默认是ASC

下面的代码说明Person和Book之间是一对多关系集合books按照Book的isbn升序,name降序排列。

  @Table(name = "MAPKEY_PERSON")
    public class Person {

	@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
	@OrderBy(name = "isbn ASC, name DESC")
	private List books = new ArrayList();
    }

PrimaryKeyJoinColumn 

在三种情况下会用到PrimaryKeyJoinColumn

  • entity class映射到一个或多个从表从表根据主表的主键列(列名为referencedColumnName值的列),建立一个类型一样的主键列,列名由name属性定义
  • one2one关系,关系维护端的主键作为外键指向关系被维护端的主键,不再新建一个外键列

元数据属性说明:

  • name:列名
  • referencedColumnName:该列引用列的列名
  • columnDefinition: 定义建表时创建此列的DDL

下面的代码说明Customer映射到两个表,主表CUSTOMER,从表CUST_DETAIL,从表需要建立主键列CUST_ID,该列和主表的主键列id除了列名不同,其他定义一样

								  @Entity
    @Table(name="CUSTOMER")
	@SecondaryTable(name="CUST_DETAIL",[email protected](name="CUST_ID",referencedColumnName="id"))
	public class Customer {
	 @Id(generate = GeneratorType.AUTO)
	  public Integer getId() {
		  return id;
	  }
	}

下面的代码说明Employee和EmployeeInfo是一对一关系,Employee的主键列id作为外键指向EmployeeInfo的主键列INFO_ID

  @Table(name = "Employee")
    public class Employee {
	@OneToOne
	@PrimaryKeyJoinColumn(name = "id", referencedColumnName="INFO_ID")
	EmployeeInfo info;
	}

PrimaryKeyJoinColumns 

如果entity class使用了复合主键,指定单个PrimaryKeyJoinColumn不能满足要求时,可以用PrimaryKeyJoinColumns来定义多个PrimaryKeyJoinColumn元数据属性说明:

  • value: 一个PrimaryKeyJoinColumn数组,包含所有PrimaryKeyJoinColumn

下面的代码说明了Employee和EmployeeInfo是一对一关系他们都使用复合主键,建表时需要在Employee表建立一个外键,从Employee的主键列id,name指向EmployeeInfo的主键列INFO_ID和INFO_NAME.

   @Entity
   @IdClass%2

Java