语言

JavaWeb

基础 #

分层
    视图层         jsp
    控制层         servlet
    业务层         过滤数据
    manager层     封装第三方,service能力下沉,dao中间件
    数据访问层对象  封装对象
    数据库
java web 13种技术
    JDBC        Java Database Connectivty
    JNDI        Java Name and Directory Interface
    EJB        Enterprise JavaBean
    RMI        Remote Method Invoke
    Java IDL/CORBA
    JSP        Java Server Pages
    Java Servlet
    XML        Extensible Markup Language
    JMS        Java Message Service
    JTS        Java Transaction Service
    JTA        Java Transaction Architecture
    JavaMail
    JAF        JavaBeans Activation Framework

jsp #

基于servlet, html页面嵌java代码,第一次访问时解释成servlet。位于视图层
域对象
    pageContext     # 当前页面有效
    request         # 一次请求范围
    session         # 会话
    application context     # 同一服务器
内置对象
    Request
    Response
    Session
    Out             # 输出流
    PageContext     # context
    Page            # jsp的this
    Exception       # <%@ page isErrorPage="true"%> 时使用,显示异常信息
    Application     # 服务器
    Config          # 服务器配置

jsp-el表达式 #

${  }
11个内置对象
    pageContext    // pageContext
    page        // map (相当于pageScope,不过写法上省略了Scope)
    requestScope    // map
    sessionScope    // map
    applicationScope    // map
    param        // map        ,用${param.name}的形式得到传递的参数
    paramValues    // map<String,String []>
    header        // map
    hearderValues    // map<String, String []>
    cookie        // map
    initParam        // map

语法
${list[0]}<br>
    ${map.mapteststring}<br>
    ${map[mapkey]}<br>
    ${map['mapteststring']}<br>
    ${request }
    ${pageContext.request.contextPath }<br>        # el表达式中访问内置对象
    ${requestScope.aaa }                # 访问内置对象requestScope,得到request作用域中的aaa元素
    ${pageContext.servletContext.contextPath }<br>
    ${param}<br>
    ${paramValues['a'] }<br>
    ${paramValues['a'][0] }
    ${paramValues['a'][1] }
    ${paramValues['a'][2] }<br>
    ${empty novalue}<br>
    ${1>2?"yes" : "no"}<br>

        #  . 与 [] 可以替换使用,但有两点需要注意
        1  .1不行,但是[1]可以
        2    1> map["key"]    是取map中"key"对应的值
            2> map[key]是先从作用域中取得key的字符串如"aaa",再取map中"aaa"对应的值
            3> .key    相当于1>中的介绍,是取map中"key"对应的值
            4> .是不能相当于2>中的介绍那样使用的
比较符${ }中使用
    empty
    not empty
    三元式(?:)
    简单的算术运算

jsp-taglib标签库 #

jstl标签库1.1 或1.2
    标签库1.1中需要    jstl.jar 与 standard.jar 库
可放入域scope的类型
    page
    request
    session
    application
    functions
el表达式级使用,其它都标签级使用
functions
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
    ${fn:contains("gzitcast", "itcast") }  <br>
    ${fn:containsIgnoreCase("gzitcast", "ITCAST") }  <br>
    ${fn:endsWith("gzitcast", "st") } <br>
    ${fn:indexOf("gzitcsat", "cs") } <br>
    ${fn:join(arr, "-") } <br>
    ${fn:length("gzitcast") } <br>
    ${fn:replace("gzitcast", "gz", "广州") } <br>
    ${fn:split("gz,it,cast", ",") } <br>
    ${fn:startsWith("gzitcsat", "gz") } <br>
    ${fn:substring("gzitcsat", 3, 8) } <br>
    ${fn:substringAfter("gzitcsat", "cs") } <br>
    ${fn:substringBefore("gzitcsat", "cs") } <br>
    ${fn:toLowerCase("gziTCsat") } <br>
    ${fn:toUpperCase("gziTCsat") } <br>
    ${fn:trim("  gzitcsat  ") } <br>
    <%-- 对字符串中进行转义处理,如:会把"<"替换为"&lt;",把">"替换为"&gt;" --%>
    ${fn:escapeXml("<h3>gzitcsat</h3>") } <br>
core
    所有标签:
        out
        set
        remove
        catch
        if
        choose
        when
        otherwise
        forEach
        url
        param
        redirect
        forTokens
        import

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/cores"%>
    <c:out var=""    default=""    escapeXml="true">    <%--    放过xml过滤,让它显示,默认不显示    --%>
    -------------------------------------
    scope方式
    <c:set    var=""    value=""    scope="">
    target方式    <%--    这个放入page作用域中的map值    --%>
    <%  Map map = new HashMap();  %>
    <c:set    property="key1"    value="key1value"    target="<%=map%>"
    <%  pageContext.setAttribute("map", map);  %>
    <c:out    value="${map[key2]}"

    -------------------------------------
    <c:catch var="e"></c:catch>
    <c:out value='<%= ((Exception)pageContext.getAttribute("e",PageContext.PAGE_SCOPE)).getMessage() %>'></c:out>
    -------------------------------------
    <c:remove    var=""    scope="">
    -------------------------------------
    <c:if test="${not empty    }" scope=""    var="">
    -------------------------------------
    <%--    if...else标签    --%>
    <c:choose>
    <c:when test="">
    <c:otherwise>
    -------------------------------------
    <c:forEach  begin=""    end=""    step=""    items=""    var=""    varStatus="state">          <%--    varStatus中的函数有first last count begin end    --%>
    <tr bgcolor='${state.count%2 == 0? "red" : "pink"}' >
    </c:forEach>

    varStatus可用的函数
        current    // 当前这次迭代的项
        index    // 索引
        count    // 计数
        first        // 第一个
        last        // 最后一个
        begin    // begin属性值
        end        // end 属性值
        step        // step属性值
    -------------------------------------
    uri 代表所有协议路径

    <c:url    var="itcast"    value="http://www.itcast.cn"    scope="page"    context=""    >    <%--    context 是整个网站    --%>
    <c:param    name="name"    value="中文">    <%--    这样传参数有编码    --%>

    如果value值为"/" 则加入context属性提供上下文名称,如果context也被省略,就使用当前servlet的上下文名称
    -------------------------------------
    <c:redirect    url="${itcast}"    context=""    >
    -------------------------------------
    <c:set    var="name"    value="xx,xxx,xxx,xx"    scope="request"    >
    <c:forTokens    items="${name}"    delims=","    begin=""    end=""    step="1"    var="name"    varStatus=""    >    <%--切割字符串--%>
    -------------------------------------
    <c:import    url="/publics/head.jsp"    >    <%--动态包含,引入公共文件--%><%--网站publics文件夹--%>


sql标签库
    # 以前没有mvc模式的时候,通过页面访问数据库时用的,现在不用
    引入
        <%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
    设置数据源
        <sql:setDataSource dataSource=”dataSource”[var=”name”]
                            [scope=”page|request|session|application”]/>
    jdbc连接
        <sql:setDataSource driver=”driverClass” url=”jdbcURL”
                user=”username”
                password=”pwd”
                [var=”name”]
                [scope=”page|request|session|application”]/>

    JSTL提供了<sql:query>、<sql:update>、<sql:param>、<sql:dateParam>和<sql:transaction>这5个标签
        1.query:
        query必需指定数据源
        <sql:query sql=”sqlQuery” var=”name” [scope=”page|request|session|application”]
        [dataSource=”dateSource”]
        [maxRow=”maxRow”]
        [startRow=”starRow”]/>
        或
        <sql:query var=”name” [scope=”page|request|session|application”]
        [dataSource=”dateSource”]
        [maxRow=”maxRow”]    # 设定最多可以暂存数据的长度
        [startRow=”starRow”]    # 设定从哪一行开始
                >
                sqlQuery
                </sql:query>

        结果集的参数
            rowCount    # 结果集中的记录总数
            rowsByIndex    # 以数字为作索引的查询结果
            columnNames    # 字段名称数组
            Rows    # 以字段为索引查询的结果
            limitedByMaxRows    # 是否设置了maxRows属性来限制查询记录的数量
                limitedByMaxRows用来判断程序是否收到maxRows属性的限制。
                并不是说设定了maxRows属性,得到结果集的limitedByMaxRows的属性都为true,
                当取出的结果集小于maxRows时,则maxRows没有对结果集起到作用此时也为false。
                例如可以使用startRow属性限制结果集的数据量。

        2.update:
        </sql:update>
        <sql:update sql=”SQL语句” [var=”name”] [scope=”page|request|session|application”]
                [dateSource=”dateSource”]/>
        或
    <sql:update [var=”name”] [scope=”page|request|session|application”]
                [dateSource=”dateSource”]
                    >
                    SQL语句
        参数说明
            dataSource    # 数据源对象
            其它与query一样

        3.param 参数设置
        <sql:param value=”value”/>
        或
        <sql:param>
            Value
            </sql:param>

        4.dataParam 标签    # 用于为SQL标签填充日期类型的参数值

        参数说明
            value:java.util.Date类型的参数。
            type属性:指定填充日期的类型timestamp(全部日期和时间)、time(填充的参数为时间)、date(填充的参数为日期)。

        5.transaction 标签

        <sql:transaction [dataSource=”dataSource”]
            [isolation=”read_committed|read_uncommitted|repeatable|serializable”]
            >
            <sql:query>
            <sql:uptade>
        </sql:transation>

xml标签库
    核心操作
    out    # 主要用来取出XML中的字符串。
        属性
        select    # XPath语句
        escapeXml    # 是否转换特殊字符

    parse    # 用来解析xml文件
        属性
        doc    # XML文件
        systemId    # XML文件的URL
        filter    # XMLFilter过滤器
        varDom    # 储存解析后的XML文件
        scopeDom    # varDom的范围

    set    # 将从XML文件取得的内容储存至属性范围中
        属性
        select    # XPath语句

    流程控制
    if
    choose when otherwise
        属性
        select    # XPath语句
    文件转换
    <x:transform doc=”xmldoc” xslt=”XSLTStytlesheet”[docSystemId=”xmlsystemid”]
        [result=”result”]
        [var=”name”]
        [scope=”scopeName”]
        [xsltSystemId=”xsltsystemid”]/>
    或
    <x:transform doc=”xmldoc” xslt=”XSLTStytlesheet”[docSystemId=”xmlsystemid”]
        [result=”result”]
        [var=”name”]
        [scope=”scopeName”]
        [xsltSystemId=”xsltsystemid”]
        >
        <x:param/>
        </x:transform>
    或
    <x:transform doc=”xmldoc” xslt=”XSLTStytlesheet”[docSystemId=”xmlsystemid”]
        [result=”result”]
        [var=”name”]
        [scope=”scopeName”]
        [xsltSystemId=”xsltsystemid”]
        >

        属性
        doc    # 指定xml文件来源
        xslt    # 转化xml的样式模板
        docSystemId    # xml文件的URI
        xsltSystemId    # xslt文件的URI
        result    # 用来存储转换后的结果对象

国际化
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    国际化标签
    1.setLocale    # 设置一个全局的地区代码,设定的是本地的环境
        中文-大陆:<fmt:setLocale value="zh_CN"/> <fmt:formatDate value="${todayValue}"/><br>

    2.requestEncoding    # 设置统一的请求编码
        <fmt:requestEncoding value="GB2312"/>

    信息显示标签
    1.<fmt:bundle> 设置临时要读取的资源文件
    2.<fmt:message>  通过key取得value
    3.<fmt:setBundle>  设置一个要读取的全局的资源文件
        如
        <fmt:setBundle basename="applicationMessage" var="MyResourse"/>    # 绑定了名为applicationMessage_zh_CN.properties一类 的文件
        <fmt:bundle basename="MyResourse" prefix="label.">
        <fmt:message key="backcolor" bundle="${applicationBundle}"/>
        <fmt:message key="fontcolor" />
    </fmt:bundle>


    数字及日期格式化标签
    1.<fmt:formatDate>  日期的格式化
        属性
        value:格式化的日期,该属性的内容应该是 java.util.Date 类型的实例
        type:格式化的类型
        pattern:格式化模式
        timeZone:指定格式化日期的时区
    2.<fmt:parseDate>  解析日期
        属性
        value:将被解析的字符串
        type:解析格式化的类型
        pattern:解析格式化模式
        parseLocale:以本地化的形式来解析字符串,该属性的内容为 String 或 java.util.Locale 类型的实例
        timeZone:指定解析格式化日期的时区
    3.<fmt:formatNumber>  数字格式化
        属性
        value:格式化的数字,该数值可以是 String 类型或 java.lang.Number 类型的实例
        type:格式化的类型,可能值包括:currency(货币)、number(数字)和percent(百分比)
        pattern:格式化模式
        maxIntegerDigits:指定格式化结果的最大值
        minIntegerDigits:指定格式化结果的最小值
        maxFractionDigits:指定格式化结果的最大值,带小数
        minFractionDigits:指定格式化结果的最小值,带小数
        如
        <fmt:formatNumber value="1000.888" type="currency" var="money"/>

    4.<fmt:parseNumber>  解析数字
        属性
        value:将被解析的字符串
        type:解析格式化的类型
        pattern:解析格式化模式
        如
        <fmt:parseNumber value="15%" type="percent" var="num"/>
    5.<fmt:setTimeZone>  标签则允许将时区设置保存为一个变量,在之后的工作可以根据该变量来进行属性描述
        属性
        value    # 时区的设置
        var    # 用于保存时区为一个变量
    6.<fmt:timeZone>  标签将使得在其标签体内的工作可以使用该时区设置
        属性
        value    # 时区的设置
    7.<fmt:param> 标签:用于参数传递
        如:在MyResourse.properties文件中,有一个索引值如下(其中,{0}代表占位符):
        Str2=Hi,{0}
        则,使用<fmt:param>标签传入值如下:
        <fmt:bundle basename="MyResourse">
            <fmt:message key="Str2">
            <fmt:param value="张三" />
            </fmt:message>
        </fmt:bundle>
        也可以在资源文件中指定参数的类型:
        如:在MyResourse.properties文件中,有一个索引值如下:
        Str3={0,date}
        则,使用<fmt:param>标签传入值如下:
        <% request.setAttribute("now",new Date()); %>
        <fmt:bundle basename="MyResourse">
            <fmt:message key="Str3">
            <fmt:param value="${now}" />
            </fmt:message>
        </fmt:bundle>

jsp动作标签 #

在JSP中的动作行为包括:Include、 Forward、 UseBean、 GetProperty、 SetProperty、 Plugin。

一、Include行为

    <jsp:include>标签表示动态包含一个静态的或者动态的文件。

    语法:
    <jsp:include page="path" flush="true" />
    or
    <jsp:include page="path" flush="true">
    <jsp:param name="paramName" value="paramValue" />
    </jsp:include>

    注:
    1、page="path" 为相对路径,或者代表相对路径的表达式。
    2、flush="true" 必须使用flush为true,它默认值是false。
    3、<jsp:param>子句能让你传递一个或多个参数给动态文件,也可在一个页面中使用多个<jsp:param>来传递多个参数给动态文件。
    4、<jsp:include page="" flush=""> 与<%@ include file=""%>的区别:
        <jsp:include >是动态包含<%@include%>是静态包含。
        # jsp页面是把include指令元素(<%@ include file=""%>)所指定的页面的实际内容(也就是代码段)加入到引入它的jsp页面中,合成一个文件后被jsp容器将它转化成servlet。
        ## 可以看到这时会产生一个临时class文件和一个servlet源文件。
        ## 而动作元素(<jsp:include page=""/>)是在请求处理阶段引入的,会被JSP容器生成两个临时class文件和两个servlet原文件。
        ## 而引入的只是servlet的输出结果,即JspWriter对象的输出结果,而不是jsp的源代码。

二、Forward行为

    <jsp:forward>标签表示重定向一个静态html/jsp的文件,或者是一个程序段。

    语法:
    <jsp:forward page="path"} />
    or
    <jsp:forward page="path"} >
    <jsp:param name="paramName" value="paramValue" />……
    </jsp:forward>

    注:
    1、page="path" 为一个表达式,或者一个字符串。
    2、<jsp:param> name 指定参数名,value指定参数值。参数被发送到一个动态文件,参数可以是一个或多个值,而这个文件却必须是动态文件。要传递多个参数,则可以在一个JSP文件中使用多个<jsp:param>将多个参数发送到一个动态文件中。

三、UseBean行为

    <jsp:useBean>标签表示用来在JSP页面中创建一个BEAN实例并指定它的名字以及作用范围。

    语法:
    <jsp:useBean id="name" scope="page | request | session | application" typeSpec />
    其中typeSpec有以下几种可能的情况:
    class="className" | class="className" type="typeName" | beanName="beanName" type="typeName" | type="typeName" |

    注:
    你必须使用class或type,而不能同时使用class和beanName。beanName表示Bean的名字,其形式为“a.b.c”。

四、GetProperty行为

    <jsp:getProperty>标签表示获取BEAN的属性的值并将之转化为一个字符串,然后将其插入到输出的页面中。

    语法:
    <jsp:getProperty name="name" property="propertyName" />

    注:
    1、在使用<jsp:getProperty>之前,必须用<jsp:useBean>来创建它。
    2、不能使用<jsp:getProperty>来检索一个已经被索引了的属性。
    3、能够和JavaBeans组件一起使用<jsp:getProperty>,但是不能与Enterprise Java Bean一起使用。
JavaScript通用库  Jsp语法
JSP的动作标签  2008-04-01 11:47:49|  分类: JSP学习 |  标签: |字号大
中
小 订阅
在JSP中的动作行为包括:Include、 Forward、 UseBean、 GetProperty、 SetProperty、 Plugin。

一、Include行为

<jsp:include>标签表示包含一个静态的或者动态的文件。

语法:
<jsp:include page="path" flush="true" />
or
<jsp:include page="path" flush="true">
<jsp:param name="paramName" value="paramValue" />
</jsp:include>

注:
1、page="path" 为相对路径,或者代表相对路径的表达式。
2、flush="true" 必须使用flush为true,它默认值是false。
3、<jsp:param>子句能让你传递一个或多个参数给动态文件,也可在一个页面中使用多个<jsp:param>来传递多个参数给动态文件。

二、Forward行为

<jsp:forward>标签表示重定向一个静态html/jsp的文件,或者是一个程序段。

语法:
<jsp:forward page="path"} />
or
<jsp:forward page="path"} >
<jsp:param name="paramName" value="paramValue" />……
</jsp:forward>

注:
1、page="path" 为一个表达式,或者一个字符串。
2、<jsp:param> name 指定参数名,value指定参数值。参数被发送到一个动态文件,参数可以是一个或多个值,而这个文件却必须是动态文件。要传递多个参数,则可以在一个JSP文件中使用多个<jsp:param>将多个参数发送到一个动态文件中。

三、UseBean行为

<jsp:useBean>标签表示用来在JSP页面中创建一个BEAN实例并指定它的名字以及作用范围。

语法:
<jsp:useBean id="name" scope="page | request | session | application" typeSpec />
其中typeSpec有以下几种可能的情况:
class="className" | class="className" type="typeName" | beanName="beanName" type="typeName" | type="typeName" |

注:
你必须使用class或type,而不能同时使用class和beanName。beanName表示Bean的名字,其形式为“a.b.c”。

四、GetProperty行为

<jsp:getProperty>标签表示获取BEAN的属性的值并将之转化为一个字符串,然后将其插入到输出的页面中。

语法:
<jsp:getProperty name="name" property="propertyName" />

注:
1、在使用<jsp:getProperty>之前,必须用<jsp:useBean>来创建它。
2、不能使用<jsp:getProperty>来检索一个已经被索引了的属性。
3、能够和JavaBeans组件一起使用<jsp:getProperty>,但是不能与Enterprise Java Bean一起使用。

五、SetProperty行为

    <jsp:setProperty>标签表示用来设置Bean中的属性值。

    语法:
    <jsp:setProperty name="beanName" prop_expr />
    其中prop_expr有以下几种可能的情形:
    property="*" | property="propertyName" | property="propertyName" param="parameterName" | property="propertyName" value="propertyValue"

    注:
    使用 jsp:setProperty 来为一个Bean的属性赋值;可以使用两种方式来实现。
    1、在jsp:useBean后使用jsp:setProperty:
    <jsp:useBean id="myUser" … />
    …
    <jsp:setProperty name="user" property="user" … />
    在这种方式中,jsp:setProperty将被执行。
    2、jsp:setProperty出现在jsp:useBean标签内:
    <jsp:useBean id="myUser" … >
    …
    <jsp:setProperty name="user" property="user" … />
    </jsp:useBean>
    在这种方式中,jsp:setProperty只会在新的对象被实例化时才将被执行。

    * 在<jsp:setProperty>中的name值应当和<jsp:useBean>中的id值相同。

六、Plugin行为

    <jsp:plugin>标签表示执行一个applet或Bean,有可能的话还要下载一个Java插件用于执行它。

    语法:
    <jsp:plugin
    type="bean | applet"
    code="classFileName"
    codebase="classFileDirectoryName"
    [ name="instanceName" ]
    [ archive="URIToArchive, ..." ]
    [ align="bottom | top | middle | left | right" ]
    [ height="displayPixels" ]
    [ width="displayPixels" ]
    [ hspace="leftRightPixels" ]
    [ vspace="topBottomPixels" ]
    [ jreversion="JREVersionNumber | 1.1" ]
    [ nspluginurl="URLToPlugin" ]
    [ iepluginurl="URLToPlugin" ] >
    [ <jsp:params>
    [ <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" /> ]+
    </jsp:params> ]
    [ <jsp:fallback> text message for user </jsp:fallback> ]
    </jsp:plugin>

    注:
    <jsp:plugin>元素用于在浏览器中播放或显示一个对象(典型的就是applet和Bean),而这种显示需要在浏览器的java插件。
    当Jsp文件被编译,送往浏览器时,<jsp:plugin>元素将会根据浏览器的版本替换成<object>或者<embed>元素。注意,<object>用于HTML 4.0 ,<embed>用于HTML 3.2。
    一般来说,<jsp:plugin>元素会指定对象是Applet还是Bean,同样也会指定class的名字,还有位置,另外还会指定将从哪里下载这个Java插件。

jsp函数 #

用response.getOutputStream返回数据(而非JspWriter)时,调用:
    # 如输出图片对象:ImageIO.write(image, "jpeg", response.getOutputStream());
    out.clear();        # 清空 out
    out = pageContext.pushBody()    # 将图片对象的流从out输出,直到整个输出结束(接收方网页加载全部完成时)后才断开

jsp基本 #

模板元素
脚本
<%    %>
脚本表达式
<%=    %>
注释
<%--    --%>
指令
    <%@ page%>
    language="java"
    import="java.util.*,java.io.*"
    contentType="mineType [; charset=characterSet]"
    pageEncoding="characterSet"
    session="true"
    buffer="none | 8kb | sizekb"
    autoFlush="true"
    isThreadSafe="true"
    info="text"
    errorPage="relative_url"
    isErrorPage="true"
    isELIgnored="true"
    <%@ include%>    # <%@ include file="in.jspf" %> 是静态包含(原代码中包含),一般包含名字为*.jspf的jsp文件
    <%@ taglib%>    # 标签库
声明
    <%!    %>    # 全局声明(刷新页面仍然保存数据)
    <% %>    # 局部的声明(刷新页面不保存数据)
标签
    <jsp:forward page=""></jsp:forward>
    <jsp:include page=""></jsp:include>
内置对象 9个
    pageContext
    request
    response
    config
    session
    application
    page
    out
    exception

    全局变量
    static final JspFactory        _jspxFactory
    static java.util.List        _jspx_dependants
    javax.el.ExpressionFactory    _el_expressionfactory
    org.apache.AnnotationProcessor    _jsp_annotationprocessor
    在_jspService中的变量
    HttpServletRequest        request
    HttpServletResponse        response
    PageContext        pageContext = null;
                pageContext = _jspxFactory.getPageContext(this,request,response,null,true,8192,ture);
    HttpSession        session = null;
                session = pageContext.getSession();
    ServletContext        application = null;
                application = pageContext.getServletContext();
    ServletConfig        config = null;
                config = pageContext.getServletConfig();
    JspWriter            out = null;
                out = pageContext.getOut();
    Object            page = this;
    JspWriter            _jspx_out = null;
                _jspx_out = out;
    PageContext        _jspx_page_context = null;
                _jspx_page_context = pageContext;

jsp验证码 #

实例    # 在<img>标签的src属性中指定该jsp文件即可

    ## out.clear();out = pageContext.pushBody();两条语句的作用是
    ## 使该验证码jsp文件的传输不会默认地在返回数据后中断,而是在<img>标签调用该jsp的页面加载结束之后再中断数据的传输
    <%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="image/jpeg" import="java.util.*,java.awt.*,java.awt.image.*,javax.imageio.*"%>
<%!
    // 声明区,定义产生颜色和验证内容的全局方法
    public Color getColor(){

    Random random = new Random();
    int r = random.nextInt(256);
    int g = random.nextInt(256);
    int b = random.nextInt(256);
    return new Color(r,g,b);
    }
    public String getNum(){
    String str = "";
    Random random = new Random();
    for(int i = 0; i < 4; i++){
        str += random.nextInt(10) + " ";
    }
    return str;
    }
%>
<%
    // 设置响应无缓存
    response.setHeader("pragma", "mo-cache");
    response.setHeader("cache-control", "no-cache");
    response.setDateHeader("expires", 0);
    // 图片对象,画笔对象
    BufferedImage image = new BufferedImage(80,30,BufferedImage.TYPE_INT_RGB);
    Graphics g = image.getGraphics();
    // 画背景
    g.setColor(new Color(200,200,200));
    g.fillRect(0, 0, 80, 30);
    // 画干扰线
    for(int i = 0; i < 30; i++){
    Random random = new Random();
    int x = random.nextInt(80);
    int y = random.nextInt(30);
    int xl = random.nextInt(x+10);
    int yl = random.nextInt(y+10);
    g.setColor(getColor());
    g.drawLine(x, y, x + xl, y + yl);
    }
    // 画内容
    g.setFont(new Font("serif", Font.BOLD,16));
    g.setColor(Color.BLACK);
    String checkNum = getNum();
    g.drawString(checkNum,15,20);
    // 放内容到session中,返回图片流
    session.setAttribute("validateCode", checkNum.replaceAll(" ", ""));
    ImageIO.write(image, "jpeg", response.getOutputStream());
    out.clear();
    out = pageContext.pushBody();    // 不按照jsp默认的getWriter()方法输出,用我们定义的流的方法进行输出
%>

自定义标签 #

1、JspTag 接口(标记接口,类以Serializable)
2、Tag 接口(空标签,如<img/>)
    属性:
    static int EVAL_BODY_INCLUDE        通过流执行标签体
    static int EVAL_PAGE              继续执行页面
    static int SKIP_BODY            忽略执行标签体
    static int SKIP_PAGE            忽略后面的JSP页面
    方法:
    // 生命周期方法
    int doEndTag()            当遇到标签结束的时候自动执行
    int doStartTag()            当遇到标签开始的时候自动执行
    // 实现方法
    Tag getParent()            获取当前标签的父标签处理类对象
    void release()            当事件改变的时候自动执行
    void setPageContext(PageContext pc)    设置当前的JSP上下文环境
    void setParent(Tag t)        设置当前标签的父标签对象
3、TagSupport 类(有属性的标签,如<img src=""/>)
    实现了Tag接口并且提供处理标签属性的方法(set和get)。而且内部定义了一个PageContext变量并且已经初始化开发者可以直接使用this或者super直接方法该属性。
4、BodyTagSupport类(有属性有文本内容和标签,如<img src="">aaa</img>)
    新属性
    protected  BodyContent bodyContent
    新方法
    void setBodyContent(BodyContent b)
    BodyContent getBodyContent()

    BodyContent类
        abstract String getString()    //获取标签体

    写Tag接口的标签库
    1、写Tag接口实现类
        写属性pageContext(getter 和setter),从setPageContext(PageContext pc)方法中获得该属性
        复写方法
    2、写tld文件,放到/META-INF文件夹中
        <?xml version="1.0" encoding="UTF-8"?>
        <taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

        <tlib-version>1.0</tlib-version>
        <short-name>ouru</short-name>    # 简称
        <uri>/outrun-tags</uri>    # 自定义引入标签时写的路径

        <tag>
        <name>testDate</name>
        <tag-class>outrun.util.jsp.taglib.test.DataImplTag</tag-class>
        <body-content>empty</body-content>
        </tag>

        </taglib>
    3、jsp 中引用它
        <%@ taglib prefix="ouru" uri="/META-INF/myUtil.tld" %>
    写TagSupport接口的实现类
    pageContext已内置
    定义接收属性
    tld文件中加入attribute属性
        <attribute>        属性描述的开始
        <name>pattern</name>    描述属性名
        <required>true</required>    描述属性是否是必须的
        <rtexprvalue>true</rtexprvalue>  描述属性值是否可以是输出表达式
        </attribute>
    写BodyTagSupport接口的实现类
    BodyContent body = this.getBodyContent();
    String desc = body.getString();

    tld 文件中
    <body-content>JSP</body-content>    # 有标签体,可执行脚本表达式
                        ## scriptless,有标签体,不执委脚本表达式
                        ## empty,没有标签体

Jsp2.0
    JspTag — SimpleTag — SimpleTagSupport

    SimpleTagSupport类
    该类可以直接进行操作标签的属性和标签体。
    void doTag()                遇到标签的时候自动指定
    protected  JspFragment getJspBody()          获取标签体对象
    protected  JspContext getJspContext()      获取JSP上下文环境对象
    JspTag getParent()            获取该标签的父标签处理类对象
        JspFragment类
        该类代表的标签的标签体。
        abstract  void invoke(Writer out)    输出数据到指定的流,null输出到JSP页面
    获得标签体的方法:
        Writer writer = new StringWriter();
        JspFragment jspFragment = getJspBody();
        jspFragment.invoke(writer);
        String text = writer.toString();
    项目:实现 if else 判断
    Choose.java
        private boolean tag = true;

        public boolean isTag() {
            return tag;
        }

        public void setTag(boolean tag) {
            this.tag = tag;
        }

        @Override
        public void doTag() throws JspException, IOException {
            getJspBody().invoke(null);
            super.doTag();
        }
    when.java 文件
        private boolean test = false;
        public boolean isTest() {
            return test;
        }

        public void setTest(boolean test) {
            this.test = test;
        }
        @Override
        public void doTag() throws JspException, IOException {
            Choose parent = (Choose) getParent();
            if(isTest() && parent.isTag()){
            // 条件成立
            getJspBody().invoke(null);
            // 设置父的tag为false
            parent.setTag(false);
            }
            super.doTag();
        }
    Otherwise.java 文件
        @Override
        public void doTag() throws JspException, IOException {
            Choose parent = (Choose) getParent();
            if(parent.isTag()){
            // 条件成立
            getJspBody().invoke(null);
            parent.setTag(false);
            }
            super.doTag();
        }
    tld文件
        <tag>
        <name>choose</name>
        <tag-class>outrun.util.jsp.taglib.ifelse.Choose</tag-class>
        <body-content>scriptless</body-content>    # 有标签体,可执行脚本表达式
                                ## scriptless,有标签体,不执委脚本表达式
                                ## empty,没有标签体

        </tag>

        <tag>
        <name>when</name>
        <tag-class>outrun.util.jsp.taglib.ifelse.When</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
        <name>test</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
        </attribute>
        </tag>

        <tag>
        <name>otherwise</name>
        <tag-class>outrun.util.jsp.taglib.ifelse.Otherwise</tag-class>
        <body-content>scriptless</body-content>
        </tag>



控件标签:
自定义函数库
    1 创建函数库类
    public class MyFunctions {
    public static String formatMyName(String name) {
    return "your name is " + name;
    }
    public static int add(int a, int b) {
    return a+b;
    }
    }

    2 在TLD文件中配置 (引用于目标1中的tld文件)
    <function>
    <name>formatMyName</name>
    <function-class>com.taglib.MyFunctions</function-class>
    <function-signature>java.lang.String formatMyName(java.lang.String)</function-signature>
    </function>

    <function>
    <name>add</name>
    <function-class>com.taglib.MyFunctions</function-class>
    <function-signature>java.lang.String add(int, int)</function-signature>
    </function>

    3 JSP中调用

    ${cc:formatMyName("wangfei") }
    ${cc:add(12, 34) }

jdbc #

流程
    Class.forName()     # 加载驱动
    DriverManager.getConnection()
    获得sql会话对象 Statement或PreparedStatement
    设置参数setXxx(), 执行sql,处理结果集
    关闭结果集、关闭会话、关闭连接
Statement
    execute
    executeQuery
    executeUpdate
    不要使用Statement
        容易sql注入
        代码可读性可维护性差
        PreparedStatement性能高,db缓存机制,相同预编译语句调用不再编译
PreparedStatement
    # 继承Statement, 预编译sql

事务怎么写
    编程式
    声明式: 用aop注入
三种连接
    Connection
        默认自动提交
        禁止自动提交开启事务, 后调commit
    Pooled Connection
        使用完后不用关闭
    XA Connection
        分布式事务
        XAResource获得

i18n #

页面需要获取用户信息,从数据库中取数据显示

java类中试用:
cn.itcast.resource包中
    hello_en_US.properties
    hello=hello
    hello_zh_CN.properties
    hello=编码后的“你好”
    Test.java
    #  获取信息
    Locale locale = locale.CHINA;
    #  加载资源
    ResourceBundle bundler = ResourceBundle.getBundle("cn.itcast.resource.hello",locale);
    #  取出数据
    String str = bundler.getString("hello");

jsp中
<%
    ResourceBundle bundler = ResourceBundle.getBundle("lang.properties.hello", request.getLocale());
    out.write(bundler.getString("title"));
%>
<fmt>标签
    <fmt:setLocale value="${pageContext.request.locale }" scope="page"/>
    <fmt:setBundle basename="lang.properties.hello" var="bundler" scope="page"/>
    <fmt:message bundle="${bundler }" key="title"></fmt:message>
资源转码
    native2ascii.exe
    myeclipse properties文件编辑器
ie 中得到en_US
Locale locale
    getLanguage();
    getCountry();
    getDefault();
ResourceBundle
    读取文件cn.itcast.resource.hello省略_en_US.properties
    getString(String key)
实例1:
    创建页面
    创建资源文件
    编辑页面
    request.getLocale();
    ResourceBundle.getBundle("",locale);
    getString("")
实例2:
    编辑页面
    <%@ taglib%>
    <f:setLocale scope="" value="">
    <f:setBundle basename="" var="" scope="">
    <f:message bundle="" key="" >
动态数据国际化
日期国际化
    SimpleDateFormat    #  继承DateFormat
    getDateTimeInstance
    getDateInstance
    getTimeInstance

    static int FULL
    static int LONG
    static int MEDIUM
    static int SHORT
实例3:
    cn.itcast.i18n.MyDateI18n
    DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,Locale.CHINA);
    String str = format.format(new Date());
    解析页面中的字符串
    FULL 和 LONG  和 MEDIUM 和 SHORT 的 区别
    DataFomat
    String format(Date date)
    Date parse(String source)
    创建static string2Date(String str)
    #  分析区域
    Locale locale = Locale.CHINA;

    #  分析日期的风格
    int dateType = DateFormat.SHORT;
    int timeType = DateFormat.FULL;

    #  获取对象
    DateFormat format = DateFormat.getDateTimeInstance(dateType,timeType,locale);

    #  解析
    format.parse(str);
动态数字国际化
    java.text.*;
    Number类
    NumberFormat(普通数字,货币,百分比)
    getIntegetInstance
    getCurrencyInstance
    getPercentInstance(Locale inLocale)

    format
    parse
    创建cn.itcast.i18n.MyNumberI18n
    #  获取对象
    #  getPercentInstance
    #  getCurrencyInstance
    NumberFormat format = NumberFormat.getIntegerInstance(Locale.CHINA);

    #  格式化 或解析
    long num = 10000000000L;
    #  Number num = format.parse(str);
    #  double price = num.doubleValue();
    format.format(num);
动态文本国际化
    At 12:30 pm on jul 3,1998, a hurricance destroyed 99 houses and caused $1000000 of damage.
    MessageFormat
    MessageFormat(String pattern,Locale locale)
    format(String pattern,Object...arguments)
    format(Object)
    parse()
    占位
    At{0}  on {0}, a hurricance destroyed{1} houses and caused {2} of damage.

    实例1:
    MyMessageI18n.java
    #  定义模式字符串
    String pattern
    #  定义locale对象
    MessageFormat format = new MessageFormat(pattern,Locale.CHINA);
    #  定义参数数组
    DateFormat datef = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT,Locale.US);
    Date date = datef.parse("Jul 3,1998 12:30 PM");

    Integer num = new Integer(99);

    long currency = NumberFormat.getCurrencyInstance(Locale.US).parse("$1000000");
    String damage = NumberFormat.getCurrencyInstance(locale).format(currency);

    Object [] values = {date,num,damage};
    #  格式化
    String str = format.format(values);

    分析:{索引,对象,类型}
    MessageFormat messf = new MessageFormat("{0,time,short} on {0,date}, a hurricance destroyed {1} houses and caused {2,number,currency} of damage.",Locale.CHINA);

    Object [] values = {new Date(),new Integer(100),1000};

    String str = messf.format(values);

中间件 #

顺序
    监听器 -> 过滤器

监听器 #

分类
    ServletContextListener
    ServletContextAttributeListener
    HttpSessionListener
    HttpSessionAttributeListener
    HttpSessionActivationListener
web.xml 注册在过滤器后面,servlet前面
    <listener>
    <listener-class>cn.listen.MyListener</listener-class>
    </listener>
自定义
    public class MyListener implements ServletContextListener {
        public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("die");
        }
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("init");
        }

        // 当过滤器被销毁时自动执行
        public void destroy(){
            System.out.println("Filterdestroyed");
        }
        // 当拦截的时候
        public void doFilter(request,response,chain){
            System.out.println("doFilter");
            System.out.println("放行目标资源");
            chain.doFilter(request,response);
            System.out.println("目标已经放行");
        }
        // 初始化的时候
        public void init(FilterConfig config){
            System.out.println("FilterInited");
        }
    }

过滤器 #

web.xml
    <filter>
        <filter-name>testFilter
        <filter-class>cn.itcast.filter.text.TestFilter
        <init-param>
            <param-name>encoding</param-name>
            <param-value>GB2312</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>testFilter
        <url-pattern>/*
    </filter-mapping>

api
    Filter接口
        ## 多个过滤器, 按web.xml中注册的顺序映射调用。servlet执行完后, 从后向前返回执行chain.doFilter之后的方法
        init
        destroy
        doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    FilterChain接口
        doFilter(ServletRequest request, ServletResponse response)
    FilterConfig接口
        private FilterConfig config = null;
        init()
            this.config = config;
        doFilter()
            String ip = this.config.getInitParameter("ip");

映射
    1.映射的url
    2.servlet的名字
        # 通配servlet <url-pattern>/servlet/*</url-pattern>
    3.为了映射servlet中的转发, 映射一个过滤器到某种传递方式
        <filter-mapping>
            <dispatcher>FORWARD
                # FORWARD 转发方式
                # REQUEST  请求方式
                # INCLUDE  包含方式
                # ERROR  错误页面
            </dispatcher>
        </filter-mapping>
设置编码
    doFilter()
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpRequest.setCharacterEncoding("utf-8")
        String method = httpRequest.getMethod();
        if("get".equalsIgnoreCase(method)){
            chain.doFilter(new MyWapperRequest(httpRequest),response);
        }else{
            request.setCharacterEncoding("utf-8");
        }

    class MyWapperRequest extends HttpServletRequestWrapper{
        private HttpServletRequest request = null;

        public MyWapperRequest (HttpServletRequest request){
            super(request);
            this.request = request;
        }

        @Override
        public String getParameter(String name){
            String value = request.getParameter(name);
            String method = request.getMethod();
            if(value != null &&"get".equalsIgnoreCase(method)){
                value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding())
            }
            return value;
        }
    }

设置缓存
    # Expires:-1
    # Cache-Control:no-cache
    # Pragma:no-cache

    NoCacheFilter implements Filter # 设置不缓存
        doFilter()
            HttpServletResponse httpResponse = response;
            httpResponse.setHeader("Expires",-1 + "");
            //  setDataHeader("expires",-1);
            httpResponse.setHeader("cache-control","no-cache");
            httpResponse.setHeader("pragma","no-cache");
            chain.doFilter(request,httpResponse);



    CacheFilter
        # 缓存静态资源
        # web.xml中url-pattern 可以映射多个 param-name=jpg param-value=2
        private FilterConfig config = null;

        doFilter()
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            HttpServletResponse httpRequest = (HttpServletRequest) request;

        String resource = request.getRequestURI();
        String date  = null;
        if(resource.endsWith("jpg")){
            date =  config.getInitParameter("jpg");
            httpResponse.setDateHeader("expires",System.currentTimeMillis() + longDate * 60 * 60 * 1000);    // 换算成秒
        } else if(resource.endsWith("js")){
            String date  = config.getInitParameter("js");
            config.getInitParameter("js");
            httpResponse.setDateHeader("expires",System.currentTimeMillis() + longDate * 60 * 60 * 1000);
        }

        chain.doFilter(httpRequest,httpResponse);

修改编码
    EncodingFilter implements Filter{
        doFilter(request,response){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            HttpServletResponse httpRequest = (HttpServletRequest) request;
            httpRequest.setCharacterEncoding("utf-8");
            chain.doFilter(httpRequest,httpResponse);
        }
    }
    class EncodingRequest extends HttpServletRequestWrapper{
        private HttpServletRequest request;
        public EncodingRequest (HttpServletRequest request){
            super(request);
            this.request = request;
        }

        @Override
        public String getParameter(String name){
            String value = request.getParameter(name)
            if(value != null && "get".equalsIgnoreCase(request.getMethod)){
                value = new String(value.getBytes("iso8859"),"utf-8");
            }
            return value;
        }
    }

登录
    LoginFilter
        init(){
            this.config  = config;
        }
        private FilterConfig config = null;
        doFilter(){
            String path = this.config.getInitParameter("loginPage");
            HttpSession session = httpRequest.getSession(false);

            // 判断用户请求的是否是UserServlet
            String servletName = httpRequest.getServletPath();
            servletName = substring(servletName.lastIndexOf("/")+1);
            if("UsersServlet".equals(servletName)){
            }else{ // 一般的servlet
                if(session != null){
                    // 获取登录标记
                    User user = null;
                    user = (User)session.getAttribute("user");

                    // 判断
                    if(user != null){
                        // 放行资源
                        chain.doFilter(httpRequest,httpResponse);
                    }else{
                        // 页面重定向到登录页面
                        httpResponse.sendRedirect(httpRequest.getContextPath() + "/" + path);
                    }
                }
            }
        }
    web.xml
        <filter>
            <filter-name>loginFilter
            <filter-class>
            <init-param>
                <param-name>loginPage
                <param-value>
        <filter-mapping>
            <filter-name>
                <url-pattern>/jsps/*
                <url-pattern>/servlet/*
                <url-pattern>/publics/*
            <filter-name>
                <url-pattern>/publics/head.jsp
                <dispatcher>INCLUDE
                <dispatcher>FORWARD
                <dispatcher>ERROR
                <dispatcher>REQUEST
                    # 默认是request,当加上其它参数时(如include),request会没有,所以要加两个forward,request
                    ## 是指向里面以该方法请求的时候进行过滤

struts2 #

Java并发

基础 #

并发编程三大特性
    可见性(visibility)
        synchronized会刷新线程栈
    有序性(ordering)
    原子性(atomicity)

CPU #

PC寄存器(Program Counter Register, 存下一指令)
优化
    乱序读
    合并写
    乱序执行(流水线)
ALU
    访问速度
        Registers: < 1ns
        L1 cache(核内): 1ns
        WC(Writer Comblining) Buffer    # 合并写, 一般4个字节
        L2 cache(核内): 3ns
        L3 cache(CPU内): 15ns
        memory: 80ns
局部性原理
    空间
        按块读取(cache line)
            一次读64Bytes               # disruptor RingBuffer实现前后7个long,两个元素不同行,避免缓存一致性协议的通知
            Java1.8注解@Contended       # 保证不在同一行,需要JVM参数-XX:-RestrictContended
    时间
        批量读指令
内存屏障                        # 不同CPU不一样
    Intel
        sfence                      # save, 之前写操作必须完成
        lfence                      # load, 之前读操作必须完成
        mfence                      # mix(save + load)
lock指令                            # 指令执行完之前,锁内存
    lock_add

CPU缓存一致性协议 #

# 是缓存锁。无法被缓存、跨越多个缓存的数据,依然总线锁
状态
    MESI Cache一致性协议                    # Intel CPU,实现方式为主动监听
        Modified                            # 一行数据在CPU Modified, 其它CPU为Invalid
        Exclusive                           # 独享,其它CPU没有
        Shared                              # 别人也在读
        Invalid                             # 别人改过了
    MSI
    MOSI
缓存行                                      # 多数64字节
    伪共享                                  # 缓存行内无关数据也要同步

OS #

进程
    # 资源分配
线程
    # 调度执行
    切换(Context Switch)
        CPU导出原线程指令和data到cache, 再导入新线程数据
    线程数
        N(threads) = N(CPU) * U(CPU) * (1 + W/C)
            # N(CPU): CPU数
            # U(CPU): 期望CPU利用率, 0到1
            # W/C: wait时间/compute时间, (1+W/C)即运行时间/计算时间。用profiler、arthas分析
        压测决定
CPU性能压榨
    单进程
    批处理
    多进程切换
    多线程进程内切换
        I/O复用
    协程(纤程、绿色线程)用户空间切换

JVM #

内存屏障 #

LoadLoad
StoreStore
LoadStore
StoreLoad

乱序执行(指令重排序) #

为什么
    CPU快, 指令1阻塞时,指令2先执行
判断
    代码不影响单线程最终一致性
例子,多线程
        static int x = 0, y = 0;
        static int a = 0, b = 0;
        void main(){
            for (long i = 0; i < Long.MAX_VALUE; i++>) {
                x=0;
                y=0;
                a=0;
                b=0;
                CountDownLatch latch = new CountDownLatch(2);

                Thread one = new Thread(() -> {
                    public void run() {
                        a = 1;
                        x = b;
                        latch.countDown();
                    }
                });
                
                Thread other = new Thread(() -> {
                    public void run() {
                        b = 1;
                        y = a;
                        latch.countDown();
                    }
                });
                one.start();
                other.start();
                latch.await();
                if (x == 0 && y == 0) {
                    break;
                }

            }
        }
例子,类初始化指令换顺序
        class T {
            int m = 8;
        }
        T t = new T();
        汇编码
            0 new #2 <T>
                # 变量半初始化状态为0
            3 dup
            4 invokespecial #3 <T.<init>>
            7 astore_1
                # 4,7乱序执行, 先建立了关联再初始化, 变量中间状态值为0线程访问时中间状态逸出
            8 return
        class C {
            private int num = 0;
            public C() {
                new Thread(() -> System.out.println(this.num)).start();
            }
            void main() {
                new C();
                System.in.read();
            }
        }

Unsafe类 #

# 相当于指针。1.8只能根部类用, 11可以直接用
操作内存
    allocateMemory
    putXX
    freeMemory
    pageSize
生成类实例
    allocateInstance
操作类或实例
    objectFieldOffset
    getInt
    getObject
CAS
    compareAndSwapObject()          # JDK1.8
    compareAndSetObject()
    weakCompareAndSetObject()       # JDK11

修饰符与锁 #

概念 #

锁细化                              # 少代码加轻量锁
锁粗化                              # 锁太多时,如行锁变表锁

synchronized #

实现方式
    JVM没要求实现方式
    早期都是OS调度
    HotSpot
        在对象头(64位)上用2位实现,组合成锁的类型
        锁升级                      # 不比原子类慢,升完不降
            偏向锁, 第一个线程第一次访问只记线程id
            自旋锁,线程争抢时,JDK6旋10次,现在为CPU内核数一半。非公平
            重量级锁,OS调度,线程WAIT。符合执行时间长,线程多的场景
原子性、可见性
可重入                              # 同一个对象同线程可重入
加锁对象
    方法锁和对象锁锁this
    static方法锁和类锁锁class类
    继承时锁的子对象(因为是this), 调super synchronized方法也锁子对象
使用注意
    抛异常立即释放锁,但被锁数据已更新
    不能用的对象
        String常量,可能未知地方锁同一个
        Integer、Long等基本类型, 值变化会生成新对象
    synchronized的属性加final防止赋值

volatile #

# 用synchronized性能差不多,volatile一般不用
作用
    # 没有原子性,可能写同一值
    变量在线程见可见性
        依靠CPU缓存一致性协议
    禁止指令重排序                  # 用JVM的读写屏障
        
修饰引用类型,内部属性不监控

DCL(Double Check Lock)单例volatile问题
        private static volatile C c;    // 禁止了创建c指令重排序
        private C(){}
        public static C getInstance() {
            if (c == null) {
                synchronized (C.class) {
                    if (c == null) {
                        // 申请内存(半初始化状态默认0),成员变量初始化,赋值
                        // 先赋值未初始化时,线程2判断非空,返回了半初始化状态的对象
                        c = new C();    
                    }
                }
            }
            return c;
        }

CAS #

# Compare And Set/Swap, 无锁优化, 乐观锁, 自旋
# Unsafe类支持
CPU原语
    cas(V, Expected, NewValue)
        if V == E                   # 无并发值判断问题,原语上加了屏障
        V = New
        else try again or fail
Java
    AtomicInteger
        incrementAndGet()
ABA问题
    # 线程1读取标记, 线程2改过又改回来,线程1判断标记锁住了提交了业务数据
    版本号                          # Java版本号类AtomicStampedReference
LongAdder
    LongAdder每次加数字, LongAccumulator用lambda
    分段锁(CAS)。值分开放数组里, 多线程对应一个item
性能测试
    LongAdder(713) > Atomic(2166) > Synchronized(3129)

#

AQS #

# AbstractQueueSynchronizer, CLH(Craig, Landin, and Hagersten)队列锁的变种
# 实现方式: CAS,volatile, 模板方法
类图
    AbstractQueueSynchronizer
        Sync
            NonfairSync
方法
    AbstractQueueSynchronizer
        # 一个state和一个双向链表,双向链表看前一结点状态(如持有时等待)
        Node
            volatile Node prev
            volatile Node next
            volatile Thread thread
        VarHandle
            # JDK1.9,保存引用,普通属性原子操作。
            # 相比反射,直接操作二进制码
            get()
            set()
            compareAndSet()         # 原子性
            getAndAdd()             # 原子性
            class C {
                int x = 0;
                private static VarHandle handle;
                static {
                    handle = MethodHandles.lookup().findVarHandle(C.class, "x", int.class)
                    handle.compareAndSet(c, 0, 1);

                }
            }
        volatile state              # 多态实现
        acquire()
        tryAcquire()                # 模板方法
        acquireQueued()             # 获得
        addWaiter(Node.EXCLUSIVE)   # 放入队列,排他锁或共享锁, CAS设置tail(从前锁整表)
        cancelAcquire()             # status CANCELLED, tail时设置null, 非tail时unpark下一节点
    NonfairSync
        nonfairTryAcquire()

ReentrantLock #

    # 可重入锁,CAS实现, state记重入多少次
    new ReentrantLock(true)         # 公平锁
    tryLock(long, TimeUnit)
    lockInterruptibly()             # 响应interrupt()标记
    newCondition()                  # 多一个等待队列

    源码
        调NonfairSync

CountDownLatch #

    # 比join()灵活
    new CountDownLatch(4)
    countDown()
    await()

CyclicBarrier #

    # 满了一起放行, 场景如I/O批量消费
    new CyclicBarrier(4, ()->{})
    await()

Phaser #

    # 阶段批量执行过滤
    class MyPhaser extends Phaser {
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {
            switch(phase) {
                case 0:
                    print("arrived" + registeredParties);
                    return false;
                case 1:
                    print("eated" + registeredParties);
                    return false;
                case 2:
                    print("hugged" + registeredParties);
                    return true;
                default:
                    return true;
            }
        }
    }
    Person implements Runnable {
        private int i;
        public Person(int i) {
            this.i = i;
        }
        public void arrive() {
            phaser.arriveAndAwaitAdvance();
        }
        public void eat() {
            phaser.arriveAndAwaitAdvance();
        }
        public void hug() {
            if (i == 0 || i == 1) {
                phaser.arriveAndAwaitAdvance();
            } else {
                phaser.arriveAndDeregister();
            }
        }

        @Override
        public void run() {
            arrive();
            eat();
            hug();
        }
    }
    phaser = new MyPhaser();
    phaser.bulkRegister(5);
    for (int i = 0; i < 5; i++) {
        new Thread(new Person(i)).start()
    }

ReadWriteLock #

    # 读锁是共享锁,不能写,悲观锁
    # 写锁是排他锁,不能读写
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    Lock readLock = readWriteLock.readLock();
    Lock writeLock = readWriteLock.writeLock();
    void read(Lock lock) {
        lock.lock()
        lock.unlock()
    }
    void write(Lock lock) {
        lock.lock()
        lock.unlock()
    }
    for (int i =0; i<10;i++) {
        new Thread(()->read(readLock)).start();
    }
    for (int i =0; i<2; i++) {
        new Thread(()->write(writeLock)).start();
    }

StampedLock #

    # 读时允许写,读完判断不一致再读一遍,乐观锁

Semaphore #

    # 信号量, 限流同时运行, 用于线程间同步。可设置公平
    Semaphore s = new Semaphore(1, true)
    new Thread(() -> {
        s.acquire();
        s.release()
    })

Exchanger #

    # 线程间通信, 阻塞交换
    exchange()

LockSupport #

    # 线程间通信,非锁阻塞,指定线程唤醒
    # 线程启动后,unpark()可以在park()前调用生效, make(chan struct{}, 1)
    Thread t = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                LockSupport.park();
            }
        }
    })
    t.start();
    TimeUnit.SECONDS.sleep(1);
    LockSupport.unpark(t);

线程及线程池 #

使用注意 #

为什么不用Executors线程池
    用LinkedBlockingQueue超数量OOM
    拒绝策略
    线程命名

#

    Object
        wait()                              # 释放synchronized锁并加入等待队列唤醒后执行需要得到synchronized锁
        notify()                            # 只唤醒不释放当前synchronized锁
    Thread
        static currentThread()
        static sleep()
        static yield()                      # 让出CPU, 进Ready队列
        start()
        getState()
        join()                              # 等待结束
        setDaemon()                         # 是否后台
        setPriority(Thread.NORM_PRIORITY)   # 优先级没有用


    interface Runnable
        void run()
    interface Callable
        V call() throws Exception
    interface Future
        get()
        get(long, TimeUnit)
        cancel(boolean)
        isCanceled()
        isDone()
        interface RunnableFuture
            class FutureTask
        interface CompletableFuture         # parallel
            static CompletableFuture<U> supplyAsync()
            static CompletableFuture<Void> allOf(CompletableFuture<U>...)
            static CompletableFuture<Void> anyOf(CompletableFuture<U>...)
            T join()
            CompletableFuture<U> thenApply()
            CompletableFuture<Void> thenAccept(Consumer<T>)

    interface ThreadFactory
        Thread newThread(Runnable)
        class DefaultThreadFactory
    interface Executor
        void execute()
        interface ExecutorService
            shutdown()
            shutdownNow()
            isShutdown()
            isTerminated()
            awaitTermination(long, TimeUnit)
            Future submit(Callable<T>)
            Future submit(Runnable, T)                     # 手动设个result
            submit(Runnable)
            invokeAll(Collection<Callable<T>>)
            invokeAll(Collection<Callable<T>>, long, TimeUnit)
            invokeAny(Collection<Callable<T>>)
            invokeAny(Collection<Callable<T>>, long, TimeUnit)
            abstract AbstractExecutorService
                RunnableFuture<T> newTaskFor(Runnable, T)
                RunnableFuture<T> newTaskFor(Callable<T>)
                T doInvokeAny(Collection<Callable<T>>, boolean timed, long)
                submit()
                invokeAll(Collection<Callable<T>>)
                    # 忽略CancellationException, ExecutionException其它异常抛出并取消未完成任务
                invokeAll(Collection<Callable<T>>, long, TimeUnit)
                    # 忽略CancellationException, ExecutionException, TimeoutException其它异常抛出并取消未完成任务
                invokeAny(Collection<Callable<T>>)
                invokeAny(Collection<Callable<T>>, long, TimeUnit)
                class ThreadPoolExecutor                    # 线程池+任务队列
                    # 任务顺序: 核心线程, 任务队列起新线程拒绝策略
                    class ScheduledThreadPoolExecutor       # 用DelayedWorkQueue
                        scheduleAtFixedRate(()->{}, int initial, int period, TimeUnit)
                class ForkJoinPool
                    execute(ForkJoinTask)
            interface ScheduledExecutorService
                    [class ScheduledThreadPoolExecutor]
    interface CompletionService                             # 不阻塞全部任务已有结果入队列
        poll()
        class ExecutorCompletionService


    static class Executors
        newSingleThreadExecutor()                           # 为了用任务队列和生命周期管理
        newCachedThreadPool()                               # 超时60s, max为MAX_VALUE, 任务不堆积场景
        newFixedThreadPool()
        newScheduledThreadPool()                            # AbstractQueuedSynchronizer
        newWorkStealingPool()                               # ForkJoinPool, go的M,G,P
            # 每个线程单独队列, 尾部偷加尾部

创建线程 #

# 继承
class MyThread extendws Thread {
    @Override
    public void run(){}
}
new MyThread().start();

# 组合
class MyRun implements Runnable {
    @Override
    public void run(){}
}
new Thread(new MyRun()).start();

# 返回值
class myCall implements Callable<String> {
    @Override
    public String call(){}
}
FutureTask = ft = new FutureTask<String>(new MyCall())
new Thread(ft).start();
ft.get();

# 线程池
// execute无返回值
ExecutorService service = Executors.newCachedThreadPool()
service.execute(()->{});
// submit有返回值 
Future<String> f = service.submit(new MyCall());
service.shutdown();

线程状态 #

NEW
RUNNABLE            # 可调度
    READY
    RUNNING
WAITING             # 等待唤醒,忙等待(一直占CPU)
    o.wait()
    t.join()
    LockSupport.park()
    Lock.lock()

    o.notify()
    o.notifyAll()
    LockSupport.unpark()
    Lock.unlock()
TIMED WAITING
    Thread.sleep(time)
    o.wait(time)
    t.join(time)
    LockSupport.parkNanos()
    LockSupport.parkUntil()
BLOCKING            # 阻塞等待(不占CPU但经过OS调度)
    synchronized
TERMINATED

线程打断 #

方法 
    interrupt()                 # 设置打断标记位
    isInterrupted()             # 检查标记位
    static interrupted()        # 检查当前线程标记位,并重置
检测当前线程打断标记的方法      # 抛异常并重置
    Thread.sleep()
    o.wait();
    o.join();
    ReentrantLock
        lockInterruptibly()
不检测当前线程打断标记的方法
    synchronized                # 不是代码实现检测不了
    ReentrantLock
        lock()
强制打断
    Thread
        stop()                  # 已废弃, 立即释放所有锁
        suspend()               # 已废弃,强制暂停,所有锁不释放容易死锁
        resume()                # 已废弃,强制恢复
volatile
    判断数字不准,有同步的时间延迟, interrupt()也有延迟
    也需要代码中判断, 但interrupt()有wait()等系统方法支持

线程间通信 #

通知 #

# synchronized wait() notify(), CountDownLatch, LockSupport
volatile List c = new ArrayList();
final Object lock = new Object();
new Thread(() -> {
    synchronized(lock) {
        if (c.size() != 5) {
            lock.wait();
        }
        lock.notify();              // 唤醒t1
    }
}, "t2").start();

TimeUnit.SECONDS.sleep(1);

new Thread(() -> {
    synchronized(lock) {
        for (int i = 0; i < 10; i++) {
            c.add(new Object());
            if (c.size() == 5) {
                lock.notify();
                lock.wait();        // 让出sychronized锁
            }
        }
    }
}, "t1").start();

生产消费 #

# 优化count可以用CAS加(有ABA问题)
class MyContainer<T> {
    final private List<T> list = new LinkedList<>();
    final private int MAX = 10;
    private int count = 0;

    public synchronized void put(T t) {
        while(list.size() == MAX) {
            this.wait();            // 期间可能有add() 
        }
        list.add(t);
        count++;
        this.notifyAll();           // 应该只唤醒消费者
    }

    public synchronized T get() {
        T t = null;
        while(list.size() == 0) {
            this.wait();
        }
        t = list.removeFirst();
        count--;
        this.notifyAll();           // 应该只唤醒生产者
        return t;
    }
}

# 同步容器, ReentrantLock Condition
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();

public void put(T t) {
    try {
        lock.lock();
        while(list.size() == MAX) {
            producer.await();
        }
        list.add(t);
        count++;
        consumer.signalAll();
    } finally {
        lock.unlock();
    }
}

public T get() {
    T t = null;
    try {
        lock.lock();
        while(list.size() == 0) {
            consumer.await();
        }
        t = list.removeFirst();
        count--;
        producer.signalAll();
    } finally {
        lock.unlock();
    }
    return t;
}

协程 #

quasar库                            # 需要设javaagent, 每个fiber生成栈
    fiber =  new Fiber<Void>()
    fiber.start()

并发API #

Stream #

parallelStream()                    # ForkJoinPool

ThreadLocal #

内部类
    ThreadLocalMap<ThreadLocal, Object>
        # 存在每个线程里。场景如声明式事务拿conn
        # key是弱引用指向ThreadLocal, value是强引用。
    Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
方法
    set(T)
内存泄露问题
    ThreadLocal<M> tl = new ThreadLocal();
    tl.set(new M());
    tl = null;
        # threadLocalMap中key弱引用回收, value不回收
    tl.remove();
        # 必需remove()否则内存泄露, threadLocalMap中value强引用,tl回收了也一直存在

PipedStream #

# 效率不高
PipedInputStream
    connect(PipedOutputStream)
    read(byte[])
PipedOutputStream
    write(byte[])

JMH #

# Java Microbenchmark Harness
概念
    Warmup                              # 预热
    Mesurement                          # 总执行次数
    Timeout                             # 每次执行超时时间
    Threads                             # fork线程数
    Benchmark mode                      # 模式
    Benchmark                           # 方法名
环境变量
    TEMP或TMP                           # JHM临时文件存放
使用
    @Benchmark
    @Warmup(iterations = 2, time = 3)           # 执行2次, 每次隔3秒
    @Fork(5)                                    # 多少线程
    @BenchmarkMode(Mode.Throughput)             # 显示每秒多少次
    @Measurement(iterations = 10, time = 3)     # 共测10次, 每次隔3秒
    public void testA() {

    }

Disruptor #

介绍
    CAS, 环形数组Buffer
        数组用sequence定位修改快,也避免了头尾加锁
        直接覆盖降低GC
            覆盖前有等待策略
    单机MQ
        发布订阅模式
        观察者模式
    EventFactory
        会调工厂提前分配内存, 使用时不new而是修改值,提高效率, 降低GC
使用
    class MyEvent {}
    class MyEventFactory implements EventFactory<MyEvent> {
        @Override
        public MyEvent newInstance() {}
    }
    class MyEventHandler implements EventHandler<MyEvent> {
        @Override
        void onEvent(MyEvent, long sequence, boolean endOfBatch) {}
    }
    class MyExceptionHandler implements ExceptionHandler<MyEvent> {
        @Override
        void handleEventException()
        @Override
        void handleOnStartException()
        @Override
        void handleOnShutdownException()
    }
    disruptor = new Disruptor<>(factory, 1024, Executors,defaultThreadFactory())
    disruptor = new Disruptor<>(MyEvent::new, 1024, Executors,defaultThreadFactory())
    disruptor = new Disruptor<>(factory, 1024, Executors,defaultThreadFactory(), 
        ProducerType.SINGLE, new BlockingWaitStrategy())
        # 默认ProducerType.MULTI, SINGLE可提高性能不用加锁
    // 消费
    disruptor.handleEventsWith(handler1, handler2)
    disruptor.handleEventsWith((event,seq,end)->{})
    disruptor.handleExceptionsFor(handler1).with(excptionHandler1)

    disruptor.start()

    // 生产
    ringBuffer = disruptor.getRingBuffer()
    sequence = ringBuffer.next()
    event = ringBuffer.get(sequence)
    event.set("")
    ringBuffer.publish(sequence)
    translator = new EventTranslator<>() {
        @Override
        void translateTo(event, sequence) {
            event.set("")
        }
    }
    ringBuffer.publishEvent(translator)
    ringBuffer.publishEvent((event,seq, "") -> event.set(l), "")
等待策略
    BlockingWaitStrategy                # 阻塞直到再次唤醒
    BusySpinWaitStrategy                # 自旋等待
    SleepingWaitStrategy                # sleep等待
    LiteBlockingWaitStrategy            # 同BlockingWaitStrategy减少加锁次数                
    LiteTimeoutBlockingWaitStrategy     # 同LiteBlockingWaitStrategy加超时            
    PhasedBackoffWaitStrategy
    TimeoutBlockingWaitStrategy         # 同BlockingWaitStrategy加超时                
    YieldingWaitStrategy                # 尝试100然后Thread.yield()

源码分析 #

ThreadPoolExecutor #

new ThreadPoolExecutor()                    
    int corePoolSize                    # 核心线程数, 永远存活。可设置参与回收
    int maximumPoolSize                 # 最大线程数
    long keepAliveTime                  # 生存时间
    TimeUnit
    BlockingQueue<Runnable>             # 任务队列
    ThreadFactory                       # 线程工厂, 设线程名
    RejectedExecutionHandler            # 拒绝策略
        Abort                           # 抛异常
        Discard                         # 忽略掉
        DiscardOldest                   # 忽略掉排除最久的
        CallerRuns                      # 调用者线程执行, 再多就阻塞
AtomicInteger ctl
    # 高3位线程池状态,低29位线程数量
void execute()
    判断添加核心线程
    放入队列成功
        拒绝或添加非核心线程
    添加非核心线程失败
        拒绝
boolean addWorker(Runable, boolean)
    线程数量加1
    添加Worker
        加锁
        加线程
        启动
class Worker extends AbstractQueuedSynchronizer implements Runnable
    # 本身是AQS锁, 被多任务(线程)访问
    Tread thread

ForkJoinPool #

abstract class ForkJoinTask
    ForkJoinTask<V> fork()
    V join()
    abstract class RecursiveAction          # 无返回值
        void compute()
    abstract class RecursiveTask            # 有返回值
例子
    class MyTask extends RecursiveTask<Long> {
        int start;
        int end;
        @Override
        Long compute() {
            if (end - start <= MAX_NUM) {
                return sum
            }
            subTask1 = new MyTask(start, mid)
            subTask2 = new MyTask(mid, end)
            subTask1.fork()
            subTask2.fork()
            return subTask1.join() + subTask2.join();
        }
    }
    fjp = new ForkJoinPool()
    task = new MyTask(0, nums.length)
    fjp .execute(task)
    result = task.join()

JVM

基础 #

定义
    JVM规范说明书, JVMS(Java Virtual Machine Specifications)
    Java语言规范, JLS(Java Language Specification)
    虚拟机
        指令集
        内存管理
过程
    x.java -> javac -> x.class -> ClassLoader -> (字节码解释器、JIT) -> 执行引擎
JVM语言
    Scala, Kotlin, Groovy, Clojure, jython, jruby等100多个
JVM实现
    HotSpot                 # Oracle官方, 8之后收费, OpenJDK为开源版本
    Jrockit                 # Oracle收购, 合并入HotSpot
    J9                      # IBM
    Microsoft VM
    TaobaoVM                # HotSpot深度定制
    LiquidVM                # 直接匹配专门硬件
    azul zing               # 收费,快, GC1mm, HotSpot参考写的G1
JRE, JDK
    JRE = jvm + core lib
    JDK = JRE + development kit

JVM构造 #

指标 #

吞吐量: 代码时间 / (代码时间 + GC时间)
响应时间: STW越短, 响应时间越好

指令(Instructions) #

分类
    基于栈的指令类          # 简单, HotSpot
    基于寄存器的指令集      # 复杂但快, HotSpot局部变量表
8大原子操作(JSR-133已放弃这个描述,但指令没变化)
    lock                    # 主内存,标识变量线程独占
    unlock                  # 主内存,解锁独占
    read                    # 主内存,读到工作内存
    load                    # 工作内存,read后的值放入线程本地变量副本
    use                     # 工作内存,传值给执行引擎
    assign                  # 工作内存,执行引擎结果赋值给线程本地变量 
    store                   # 工作内存,存值到主内存给write备用
    write                   # 主内存,写变量值
方法指令                    # 在methods的Code中罗列
    aload_0                 # 变量表第0项入栈
    invokespecial #1        # 调private(无多态)的方法
    invokevirtual           # 调有多态可能性的方法
    invokestatic            # 调静态方法
    invokeinterface         # 调interface方法
    invokedynamic           # 1.7加入,定义类似函数指针时生成(但每个函数都创建了类)
        调用动态产生的类
            lambda
            反射
            scala等JVM语言
            CGLib ASM
        组成
            bootstrapMethod
            方法签名
        <1.8的bug           # 类产生于Perm Space,内存不回收
            for(;;) {I j = C::n;}
    return                  # 方法返回
    bipush 8                # byte扩展成int类型,放到方法栈中
    sipush 200              # short
    istore_1                # 出栈,放到下标为1的局部变量表
    iload_1                 # 局部变量表下标1位置值压栈
    iinc 1 by 1             # 局部变量表1位置值+1
    iadd                    # 出栈两个,相加压栈
    new                     # new对象, 地址压栈
    dup                     # 复制栈顶并压栈
    pop                     # 弹出栈顶
    if_icmpne 7             # int值比较,不等时跳到第7条指令
    mul                     # 乘法
    sub                     # 减法

class结构 #

工具 #

javap -v a.class
jetbrain jclasslib
jetbrain BinEd
JBE                         # 可编辑

二进制 #

Magic Number(4字节)
    cafe babe
Minor Version(2字节)        # 小版本
Major Version(2字节)        # 大版本
    JDK1.7是51.0
    JDK1.8是52.0
constant_pool_count(2字节)
    # 长度constant_pool_count-1的表
constant_pool               # 索引、tag、类型
    1 CONSTANT_Utf8_info                        # 存一些描述字符串
    2 标记
    3 CONSTANT_Integer_info
    4 CONSTANT_Float_info
    5 CONSTANT_Long_info
    6 CONSTANT_Double_info
    7 CONSTANT_Class_info
    8 CONSTANT_String_info
    9 CONSTANT_Fieldref_info                
    10 CONSTANT_Methodref_info                  # 方法引用
        指向CONSTANT_Class_info
        指向CONSTANT_NameAndType_info
    11 CONSTANT_InterfaceMethodref_info
    12 CONSTANT_NameAndType_info                # 方法名与类型
    15 CONSTANT_MethodHandle_info
    16 CONSTANT_MethodType_info
    18 CONSTANT_InvokeDynamic_info
access_flags(2字节)         # bitmap按位与组合
    # class的修饰符
    ACC_PUBLIC 0x0001 public
    ACC_FINAL 0x0010 final
    ACC_SUPER 0x0020 JDK1.0.2之后必须为真, 表示invokespectial用新语义
    ACC_INTERFACE 0x0200 是否接口
    ACC_ABSTRACT 0x0400 抽象类
    ACC_SYNTHETIC 0x1000 编译器自动生成
    ACC_ANNOTATION 0x2000 
    ACC_ENUM 0x2000 
this_class(2字节)
    存名字对应指向常量池序号
super_class(2字节)
    存名字对应指向常量池序号
interfaces_count(2字节)
interfaces
fields_count(2字节)
fields
    access_flags(2字节)
    name_index              # 存常量池索引
    descriptor_index
        byte B
        char C
        double D
        float F
        int I
        long L
        short S
        boolean Z
        void V
        Object Ljava/lang/Object
        数组
            一维数组 [B
            多维数组 [[C
    attributes_count        # 赋加属性
    attributes
methods_count(2字节)
methods
    access_flags(2字节)
    name_index
    descriptor_index        # 先参数列表,后返回值
        void m() -> ()V
        String toString() -> Ljava/lang/String;
    attributes_count
    attributes              # 赋加属性
        Code                # 指令列表, 一般先压栈this(aload_0)
            LineNumberTable
            LocalVariableTable
attributes_count(2字节)
attributes

Agent #

例子
    打包 a.jar
        MANIFEST.MF
            Premain_Class: MyAgent
        public class MyAgent {
            public static Instrumentation inst;
            public static void premain(String agentArgs, Instrumentation _inst) {
                inst = _inst;
            } 
        }
    JVM参数 -javaagent: a.jar
    使用 MyAgent.inst

JMM(Java Memory Model) #

内存 #

运行时区域 #

# Runtime data areas
分类
    Program Counter             # 程序计数器,下一条指令位置
    Method Area                 # 方法区,线程间共享
        存储
            Class元信息
            代码编译信息, JIT编译信息
            常量池(Runtime Constant Pool)           # 常量池在运行时存放区
        版本区别
            Perm Space(<1.8)    # 要设定大小, 会溢出报错
                存字符串常量
                lambda生成临时类永远存在
                Full GC不清理
            Meta Space(>=1.8)   # 自动大小无上限
                字符串常量位于堆
                会触发Full GC
    JVM stacks                  # 线程栈 
        Frame(栈帧)             # 一个方法一个栈帧
            Local Variable Table                # 局部变量表, 方法内的局部变量,值在常量池
                默认第0个为this
            Operand Stack                       # 操作数栈
            Dynamic Linking                     # 指向调用方法的 运行时常量池的符号连接
            return address                      # 当前方法执行完的返回地址
    Native Method Stacks        # C/C++方法栈
    Direct Memory               # 直接内存
    Heap                        # 堆, 线程间共享

屏障 #

CPU屏障
JVM规范
    LoadLoad                # 上load和下load不能重排
    StoreStore
    LoadStore
    StoreLoad               # 最强

对象 #

对象内存存储 #

普通对象
    对象头: markword 8字节
    ClassPointer            # 指向Class对象, 启用压缩4字节,不启用8字节
    实例数据
        引用类型            # 启用压缩4字节,不启用8字节
    Padding: 对齐8的倍数
数组对象
    对象头
    ClassPointer
    数组长度4字节
    数组数据
    Padding
对象头
    # 32位64位(25位没用到),内容不同
    锁标志位2位             # 根据锁标志位判断存储内容
        01 无锁/偏向锁
        00 轻量级锁
        10 重量级锁
        11 GC标记
    是否偏向锁1位
    剩余位 
        无锁状态
            对象hashCode(25位或31位)
                没重写过时默认计算(System.identityHashCode())
                重写过的hashCode()结果不存在这里
            分代年龄
        轻量级锁
            指向栈中锁记录的指针
        重量级锁
            指向互斥量(重量级锁)的指针
        偏向锁
            线程ID 23位
            Epoch 2位
            分代年龄4位(所以分代年龄只有15)
    其它问题
        对象计算过hashCode,对象不能进入偏向锁状态(位已经被占了)
实验工具 javaagent

对象定位 #

句柄池                      # 指向句柄,句柄有对象指针和class指针, 三色标记GC提高效率
直接指针                    # 指向对象,对象指class, HotSpot使用

并发 #

硬件层数据一致性 #

硬件结构
    L0寄存器                # 1 cycles
    L1高速缓存              # 3-4 cycles, 1ns
    L2高速缓存              # 10 cycles, 3ns
    L3高速缓存              # 40-45 cycles, 15ns, 在主板
    (QPI总线传输)           # 20ns
    L4主存                  # 60-80ns
    L5磁盘
    L6远程文件存储
数据不一致                  # 从L2多CPU开始
    锁总线(bus lock)
    CPU缓存一致性协议(如intel MESI)

volatile #

工具
    hsdis                   # HotSpot Dis Assembler, 虚拟机字节码对应汇编
bytecode
    ACC_VOLATILE
JVM
    StoreStoreBarrier
    volatile写操作          # 上边写完再写,写完下边再读,写一致
    StoreLoadBarrier

    LoadLoadBarrier
    volatile读操作          # 上边读完再读,读完下边再写,读一致
    LoadStoreBarrier
OS
    windows
        lock
    linux
        上下屏障,最后lock

synchronized #

bytecode
    方法修饰
        synchronized
    代码
        monitorenter
        monitorexit
JVM
    C/C++实现,会调用OS的同步机制
OS
    lock

happens-before原则 #

# Java要求指令不能重排的几种情况

as if serial #

# 不管如何重排序,单线程执行结果不变

过程 #

编译 #

过程
    代码 -> bytecode -> JVM指令 -> OS指令
解释器(bytecode intepreter)
JIT(Just In-Time compiler)
混合模式
    解释器 + 热点代码编译
    热点代码检测
        方法计数器
        循环计数器

加载 #

HotSpot C++代码加载
    class对象加载到MethodArea
        metaspace(JDK1.8)
        permGeneration(JDK1.8之前)
class加载过程
    loading                 # 读到内存
    linking
        verification        # 校验
        preparation         # 静态变量赋默认值
        resolution          # 解析, loadClass()可指定是否解析。常量池的符号引用转换成内存地址引用
    initializing            # 静态变量赋初始值,执行静态代码
对象加载
    new过程
        class加载
        申请对象内存
        成员变量赋默认值
        调用构造方法<init>
            成员变量顺序赋初始值
            执行构造方法语句(先super)
双亲委派                
    过程
        类名一层层向上找
        找不到时,一层层找再向下委派找
        都不能加载时, 抛ClassNotFound
    为什么
        安全, 自定义类不能覆盖
        已加载不用重复加载
    父加载器
        不是类加载器的加载器
        不是父类
        是组合的parent对象
    打破
        为什么                    
            JDK1.2之前都重写loadClass()
            thread.setContextClassLoader()指定线程上下文classLoader
            热启动/热部署(OSGi tomcat)加载同一类不同版本
        做法
            重写loadClass(), new多个ClassLoader
类加载器
    Bootstrap               # 加载核心类 lib/rt.jar charset.jar等, C++实现所以get时为null
        如加载String
    Extension               # 加载扩展jar jre/lib/ext/*.jar, 由-Djava.ext.dirs指定
    App                     # 加载classpatch指定内容
    Custom ClassLoader      # 自定义ClassLoader
加载路径环境变量            # 来自Launcher源码
    Bootstrap.ClassLoader   sun.boot.class.path
    ExtensionClassLoader    java.ext.dirs
    AppClassLoader          java.class.path
API
    Class
        getClassLoader()
    ClassLoader             # findInCache() -> parent.loadClass() -> findClass()
        private final ClassLoader parent
        loadClass           # 热加载
    Launcher
        $AppClassLoader
        $ExtClassLoader
自定义类加载器
    class MyClassLoader extends ClassLoader {
        @Override
        Class findClass(String) {
            return defineClass()
        }
    }
懒加载                      # JVM未规定什么时候加载,但规定了什么时候初始化
初始化
    new getstatic putstatic invokestatic指令,访问final变量除外
    java.lang.reflect对类进行反射调用
    初始化子类时,父类首先初始化
    虚拟机启动时,被执行的主类
    动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic, REF_putstatic, REF_invokestatic的方法句柄时, 该类初始化

GC #

引用方式(强软弱虚) #

软引用
    # 内存不够用时回收,用做缓存
    # -Xms20M -Xmx20M
    SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
    System.gc();
    m.get();
    new byte[1024*1024*15]
    m.get();
弱引用
    # 只要gc就回收,用做容器如WeakHashMap(key是弱引用), ThreadLocalMap的key
    WeakReference<M> m = new WeakReference<>(new M());
    System.gc();
    m.get();
虚引用
    # 值被回收时放入队列来通知, 用来触发回收堆外内存(用Unsafe的freeMemory())
    # 如NIO的直接内存DirectByteBuffer
    PhantomReference<M> r = new PhantomReference<>(new M(), QUEUE);
    r.get() // 自己写永远返回null                     

对象分配过程 #

向栈分配                # 不要调参数
    好处
        不必GC
    条件
        线程私有小对象
        无逃逸
        可标量替换(基本类型替换整个对象)
过大,分配到老年代
线程本地分配            # TLAB(Thread Local Allocation Buffer), 不要调参数
    好处
        为了减少线程争用
    条件
        小对象
        占用eden, 默认每个线程占1%
伊甸区
s1,s2
    次数                   # 最大15(对象头上空间决定)
        Parallel Scavenge 15
        CMS 6
        G1 15
    动态年龄
        eden + s1 复制到s2, 超过s2总内存一半时,年龄最大的对象进入老年代
    分配担保
        YGC时survivor区空间不够, 直接进入老年代
GC清除或到老年代

GC分代过程 #

YGC -> s0
YGC, eden + s0 -> s1
    年龄足够 -> old
    s区装不下 -> old
YGC, eden + s1 -> s0
old满了 -> FGC

常见的回收器 #

概念
    Safe Point              # STW时机
    没有无STW的回收器
分代
    Young
        Serial              # 第一个GC
            STW, 单线程串行回收
        Parallel Scavenge
            STW, 并行回收
        ParNew              # Parallel New
            增强PS, 以配合CMS并行回收, CMS到某阶段时PN运行
    Old
        Serial Old
            特点
                适用几十M
                mark-sweep-compact,单线程
        Parallel Old
            特点
                适用几个G
                mark-sweep-compact,多线程
        CMS                 # concurrent mark sweep, 1.4后期引入, JDK11取消
            特点
                适用20G
                多线程并行回收, 并发回收(GC与程序同时运行),降低STW时间(200ms)
            不足            # 问题多,没有版本默认CMS
                浮动垃圾
                碎片多,新对象分配不下时,使用SerialOld
                    设低GC阈值回收浮动垃圾
            清理过程
                初始标记(initial mark)
                    STW, 单线程, 标记根对象
                [预标记]                        # Card Marking, 把Card标为Dirty
                并发标记(concurrent mark)
                    不STW, 多线程, 执行多次
                重新标记(remark)                # 处理并发标记过程中的变化
                    STW, 多线程, 
                并发清理(concurrent sweep)      # 过程中产生的浮动垃圾, 下次回收
                    不STW, 多线程, 
                [整理阶段]
            日志
                [GC(Allocation Failure)[ParNew:6144K->640K(6144K)], 0.02 secs] 6585K->2770K(19840K),0.02 secs][Times:user=0.02 sys=0.00, real=0.02 secs]
                    6144K->640K(6144K): 回收前 回收后 总容量
                    6585K->2770K(19840K): 堆回收前 回收后 总大小  

                [GC (CMS Initial Mark)]
                    [1 CMS-initail-mark]
                [CMS-concurrent-mark-start]
                [CMS-concurrent-preclean-start]
                [GC (CMS Final Remark)]
                    [YG occupancy]              # 清理后年轻代占用及容量
                    [Rescan(parallel)]          # STW下标记存活对象
                    [weak refs processing]      # 弱引用处理
                    [class unloading]           # 卸载用不到的class
                    [scrub symbol table]        # 清理常量池
                    [scrub string table]        # 清理常量池
                    [1 CMS-remark]              # 清理后老年代占用及容量
                [CMS-concurrent-sweep-start]
                [CMS-concurrent-reset-start]

不分代
    G1                      # Garbage First, 1.7引入, 1.8完善, 1.9默认
        特点
            适用上百G
            STW 10ms回收
                容易预测STW时间
                低latency, 但throughput也低
            并发回收, 三色标记
            只逻辑分代, 不物理分代 
                内存分Region, Region组成不同大小块,块在逻辑分代中
                Eden和Old区的内存映射会变化
            动态新老代空间                      # 如果YGC频繁,就Young调大
                不要手工指定, 是G1预测YGC停顿时间的基准, 停顿时间通过参数设置
        概念
            CSet            # Collection Set
                可回收Region集合, 回收时存活的对象被移动
                占堆空间不到1%
            RSet            # Remembered Set
                用于找到谁引用当前对象(对象级别), 记录其他Region的引用
                赋值时有GC写屏障                # 非内存屏障
            CardTable       # YGC定位垃圾,要从Root查所有Old区对象,效率低
                Old区对象引用Young区时, bitmap标DirtyCard。YGC时只扫描DirtyCard
            MixedGC         # 默认45%, 同CMS
                初始标记
                重新标记
                筛选回收    # 筛选Region回收,有用对象复制到其它Region
        日志
            [GC pause (G1 Evacuation Pause) (young) (initial-mark)]         # 复制存活对象, initial-mark在MixedGC时有
            [GC concurrent-root-region-scan-start]                          # 混合回收
            [GC concurrent-mark-start]                                      # 混合回收
            [Full GC (Allocation Failure)]                                  # 无法evacuation时, G1中很严重
    ZGC                     # JDK11, 不分代(SingleGeneration)
        特点
            STW设计10ms, 实际1ms
            适用4T(JDK13 16T)
            内存分块(有大有小)
        概念
            没有RSet, 改进了SATB指针
    Shenandoah              # JDK11
    Epsilon                 # JDK11, debug用
    Zulu
组合
    S + SO
    PS + PO                 # 1.8默认, 10G内存10+秒
    PN + CMS

算法 #

定位
    引用计数(ReferenceCount)
        循环引用问题        # 内部互相引用,没有外部引用
    根可达算法(RootSearching)
        线程栈变量 
        静态变量
        常量池
        JNI指针             # 本地方法用到的对象
并发标记
    三色标记
        白                  # 未被标记
        灰                  # 自身被遍历到,成员变量未被遍历到
        黑                  # 自身、成员变量都被遍历到
        漏标问题 
            两个必要条件 
                黑引用白后,灰对白的引用断开
            算法
                incremental update                      # 增量更新,关注引用的增加, CMS用的
                    增加引用后,标记为灰, 重新标记阶段再扫描
                    缺点是灰色还要重新扫描
                SATB snapshot at the beginning          # 关注引用的删除, G1用的
                    开始时快照, 引用消失时,引用推到堆栈, 下次扫描还扫白色对象
                    优点是只扫描修改过的对象, 看RSet中有没有引用
    颜色指针                # 信息记在指针里
    租户隔离, Session Base GC           # Web专用, 基于session, session断开后删除
    各GC应用
        CMS
            三色标记 + Incremental Update
        G1
            三色标记 + SATB
        ZGC
            颜色指针 + 写屏障
        Shenandoah
            颜色指针 + 读屏障
清除
    标记清除(Mark-Sweep)    # 一遍标记,一遍清理, 适合老年代
        算法简单,戚对象多时效率高
        两遍扫描效率低,容易产生碎片
    拷贝(Copying)           # 存活对象copy到新内存, 旧内存直接清理,适合伊甸区(新生代)
        适用存活对象少的情况
        内存减半
    标记压缩(Mark-Compact)  # 有用的填到前边去空隙去, 适合老年代
        不会碎片,不会内存减半
        扫描两次,还要移动
分代模型
    分代模型                # -Xms -Xmx设置大小
        new/young(新生代)   # MinorGC/YGC, -Xmn设置大小, 默认占比1
            eden(伊甸)      # 默认占比8
            survivor x 2    # 默认每个占比1
        old(老年代)         # MajorGC/FullGC, 1.8默认占比2, 之前是3
            tenured(终身)
        methodArea          # 1.7永久代, 1.8元数据区
    各JVM的分代模型
        Epsilon ZGC Shenandoah不是分代模型
        G1是逻辑分代,物理不分代
        其他都是逻辑分代 + 物理分代

调优(Tuning) #

前提
    从业务场景开始
    无监控(能压测), 不调优
目标
    减少FGC
    确定倾向                        # 吞吐量, 或响应时间
        吞吐量好: PS + PO
        响应时间好: G1 或 PN + CMS  # G1吞吐量少10%
组成部分
    JVM预规划
    优化JVM运行环境(慢、卡顿)
    解决JVM运行时出现的问题(OOM)
步骤
    熟悉业务场景
        响应时间
        吞吐量
    选择回收器组合
    计算内存需求(小的快,大的少gc)
    选CPU
    设定年代大小、升级年龄
    设定日志参数
    观察日志情况

问题分析 #

工具 #

CPU经常100%
    top查进程CPU(top)
    进程中线程CPU(top -Hp)
    导出该线程堆栈(jstack)
    查哪个方法(栈帧)消耗时间(jstack)
内存高
    导出堆内存(jmap)
    分析(jhat jvisualvm mat jprofiler ...)
监控JVM
    jstat jvisualvm jprofiler arthas top ...
    网管: Ansible
流程
    网管报警
    top -Hp 进程号
    jstack 进程号               # 列出所有线程号, 线程状态
        WAITING, 一直等不到, BLOCKED, 拿不到锁
        waiting on <0x0000> (a java.lang.Object)    # 找到目标等待的线程
    jstack -l 16进制线程号      
    jps
    jinfo 进程号                # 列JVM信息
    jstat -gc 进程号 500        # 每500ms打印一次gc信息
    jmap -histo 进程号 | head -20                   # 列所有对象
        有性能消耗,但不很高,可以在线执行
    jmap -dump:format=b, file=x pid                 # 导出转储文件
        内存特别大时,jmap会卡顿
        多个服务器可用,停一个不影响
        设定HeapDumpOnOutOfMemoryError产生堆转储文件                 
        在线定位(中小型公司用不到)
    jhat -J-mx512M x.hprof      # 分析堆dump文件, 有OQL
    arthas                      # 在线定位
        启动
            java -jar arthas-boot.jar
        常用命令                # 没有集成jmap功能
            jvm                 # jinfo
            thread              # jstack
                thread 1
            dashboard           # top
            heapdump            # jmap -dump
            dump
            redefine            # 热替换
                目前只能改method实现,不能改方法名与属性
            jad                 # 反编译类
                看动态代理生成的类
                看第三方类
                看版本
            sc                  # search class, 显示class信息
            watch               # watch method
    MAT                         # 分析dump文件
    jprofiler
    jconsole                    # 需要JMX
        JMX会消耗性能生产服务器不开
        JMX图形界面只用于压测
    jvisualVM                   # 需要JMX, 可分析dump文件

内存 #

现象
    OOM崩溃
    CPU飙高, 不断FGC
线程池不当运用
加内存反而卡顿
    GC, 应该用CMS或G1替换 PS+PO
JIRA不停FGC, 没定位出来
    扩内存到50G, GC换G1, 重启
tomcat server.max-http-header-size过大
    默认4096, 每个请求都分配
lambda表达式导致方法区溢出
    java.lang.OutofMemoryError: Compressed class space
disruptor不释放缓存
使用Unsafe分配内存, 直接内存溢出
-Xss设定小, 栈溢出
重写finalize()引发GC
    finalize()耗时长, GC时回收不过来,不停GC
内存不到10%,频繁FGC
    有人显式调用System.gc()                         # 不定时调,但会频繁调
大量线程, native thread OOM
    减少堆空间,留更多系统内存产生native thread
G1产生FGC
    降低MixedGC触发的阈值       # 默认45%
    扩内存
    提高CPU                     # 回收快

HotSpot参数 #

辅助
    -XX:+PrintCommandLineFlags -version             # 打印启动参数, -version是随便一个命令
    -XX:+PrintFlagsFinal -version                   # 打印所有XX参数
    -XX:+PrintFlagsInitial      # 打印默认参数
    -XX:+PrintVMOptions         # 显示VM启动参数
    -                           # 标准参数
    -X                          # 显示非标参数
    -XX                         # 显示不稳定参数
内存
    -XX:+HeapDumpOnOutOfMemoryError                 # OOM时产生堆转储文件 
    -Xms40M                     # 堆起始大小
    -Xmx60M                     # 堆最大大小, 最好和Xms一样以免堆弹大弹小
    -Xmn                        # 年经代
    -Xss                        # 栈空间
    -XX:InitialHeapSize         # 起始堆大小,自动算
    -XX:MaxHeapSize             # 堆最大大小,自动算
内存模型
    -XX:-DoEscapeAnalysis       # 去逃逸分析
    -XX:-EliminateAllocations   # 去标量替换
    -XX:-UseTLAB                # 去tlab
    -XX:TLABSize                # 设置TLAB大小
    -XX:+PrintTLAB
    -XX:MaxTenuringThreshold    # 进老年代(升代)回收次数, 最大值15, CMS默认6,其它默认15
对象和类
    -XX:+UseCompressedClassPointers                 # class指针压缩
        开启时4字节,不开启时8字节
    -XX:+UseCompressedOops                          # 引用类型指针压缩, Ordinary Object Pointers
        开启为4字节,不开启时8字节
    -verbose:class              # 类加载详细过程
    -XX:PreBlockSpin            # 锁自旋次数
编译
    -Xmixed                     # 混合模式
    -Xint                       # 解释模式
    -Xcomp                      # 编译模式
    -XX:CompileThreshold = 10000                    # 检测热点代码次数
GC
    -XX:+PrintGC                # 打印GC信息
    PrintGCDetails              # 打印GC更详细
    PrintGCTimeStamps           # 打印GC时间
    PrintGCCauses               # GC原因
    PrintHeapAtGC
    PrintGCApplicationConcurrentTime                # GC应用程序时间
    PrintCApplicationStoppedTime                    # 打印STW时长
    -XX:+PrintReferenceGC       # 打印回收多少种引用类型
    -XX:+UseConcMarkSweepGC     # 用CMS
    -XX:+DisableExplictGC       # System.gc()不管用

    Parallel常用
        -XX:SurvivorRatio           # 新生代Eden区和Surivor区的比例
        -XX:PreTenureSizeThreshold  # 大对象到底多大
        -XX:+ParallelGCThreads      # 并发线程数, 默认是CPU数
        -XX:+UseAdaptiveSizePolicy  # 自动调所有区比例
    CMS常用
        -XX:ParallelCMSThreads      # 并发线程数,默认是CPU数一半
        -XX:CMSInitiatingOccupancyFraction 92%          # 老年代占多少时触发GC, 1.8 92%, 之前68%
            设小一点,清除浮动垃圾
            过大时,栈分配不下,Promotion Failure,触发FGC
        -XX:+UseCMSCompactAtFullCollection              # GC时压缩,避免碎片片
        -XX:CMSFullGCsBeforeCompaction                  # 多少次GC后压缩
        -XX:+CMSClassUnloadingEnabled                   # 回收方法区
        -XX:CMSInitiatingPermOccupancyFraction          # 到什么比例时进行Perm回收, 1.8之前
        GCTimeRatio                                     # GC占程序运行时间的百分比
        -XX:MaxGCPauseMillis                            # GC停顿时间, CMS会减少年轻代大小
    G1
        -XX:MaxGCPauseMillis                            # STW时间, 区别CMS, G1会调整Young区的块数
        GCTimeRatio
        -XX:GCPauseIntervalMillis                       # STW之间间隔时间
        -XX:+G1HeapRegionSize                           # Region大小, 1 2 4 8 16 32, 逐渐增大, GC间隔更长, 每次GC时间更长
            ZGC是动态调整的
        G1NewSizePercent                                # 新生代最小比例, 默认5%
        G1MaxNewSizePercent                             # 新生代最大比例,默认60%
        ConcGCThreads                                   # GC线程数
        InitiatingHeapOccupancyPercent                  # 启动GC的堆空间占用比例

JMX监控
    -Djava.rmi.server.hostname=192.168.1.1
    -Dcom.sun.management.jmxremote 
    -Dcom.sun.management.jmxremote.port=11111 
    -Dcom.sun.management.jmxremote.authenticate=false 
    -Dcom.sun.management.jmxremote.ssl=false
调优                            # 参数越来越少
    JVM参数800个
    CMS参数300个
    G1参数100个
    ZGC更少
    Zing1个
GC组合参数
    -XX:+UseSerialGC
        S + SO
    -XX:+UseParNewGC                # 已废弃
        PN + SO
    -XX:+UseConc(urrent)MarkSweepGC
        PN + CMS + SO
    -XX:+UseParallelGC               # 1.8默认
        PS + PO
    -XX:+UseParallelOldGC
        PS + PO
    -XX:+UseG1GC
        G1
日志参数
    -Xloggc:/logs/xx-xx-%t.log
    -XX:+UseGCLogFileRotation           # 5个满了,覆盖第一个
    -XX:NumberOfGCLogFiles=5
    -XX:GCLogFileSize=1024M
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -XX:+PrintGCCause

HotSpot日志 #

GC                          # PrintGCDetails
    [GC
        GC表示YGC, Full GC是FGC
    (Allocation Failure)
        原因
    [DefNew:4544k->259k(6144k), 0.0873 secs]
        DefNew表示年轻代, 回收前后的大小, 6144是年轻代总大小,回收时间
    4544k->4356k(19840k), 0.0812 secs]
        堆的前后大小, 19840是堆总空间, 回收时间
    [Times: user=0.01 sys=0.00, real=0.01 secs]
        linux time命令,用户态时间,内核态时间,总时间
异常退出dump堆
    def new generation total 6144k, used 5504k [0x00, 0x00, 0x00]
        新生代总共多少,用了多少。内存起始地址,使用空间结束地址,整体空间结束地址
        total = eden + 1个survivor
    eden space 5504k, 100% used []
        eden
    from space 640k, 0% used []
        s0
    to space 640, 0% used []
        s1
    tenured generation total 13696k, used 13312k []
        old
    the space 13696k, 97% used []
        old
    Metaspace used 2538k, capacity 4486k, committed 4864k, reserved 1056768k
        used真正使用的大小
        capacity目前指定的容量 
        committed 表示预先占用的大小
        reserved表示共保留的大小
    class space used 275k, capacity 386k, committed 512k, reserved 1048576k
        metaspace中存class的部分

Markup Language

xml #

介绍
    可扩展性标记语言,使用DTD和XML Schema标准化XML结构
    优点: 格式统一,符合标准,用于互不兼容系统
    缺点: 格式复杂,占流量大,解析占资源
    解析器
        DOM     # 树形结构
        SAX     # 事件模型
标签头
    <?xml version="1.0" encoding="utf-8"?>
命名空间
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        # xmlns:beans="http://www.springframework.org/schema/beans"  # 引用其它uri空间
        ## jar 包中的dtd/xsd文件直接用相对路径引用即可(相当于src/目录下的文件)
    <h:table xmlns:h="http://www.w3.org/TR/html4/">
       <h:tr>
       <h:td>Apples</h:td>
       <h:td>Bananas</h:td>
       </h:tr>
    </h:table>
        # 命名空间约束文件的查找
                1.联网
                2.myeclipse中的xml
                3.同目录下
                4.jar包中
        # xmlns:只能有一个没有别名
    注意
        命名空间不可以分层使用,如 <r:g:element> 是不允许的
    例子
        <?xml version="1.0" encoding="GB2312" ?>
        <c:customer xmlns:c="http://www.customer.com/">
            <c:name>ZhangSan</c:name>
            <c:phone>09098768</c:phone>
            <c:host xmlns:e="http://www.employee.com/">
                <e:name>LiSi</e:name>
                <e:phone>89675412</e:phone>
            </c:host>
        </c:customer>

        <?xml version="1.0" encoding="GB2312"?>
        <book xmlns="http://www.library.com/">
            <title>The C++ Standard Library</title>
            <author>Nicolai M.Josutis</author>
        </book>

        <?xml version="1.0" encoding="GB2312"?>
        <customer xmlns="http://www.customer.com/"
                        xmlns:e="http://www.employee.com/">
            <name>ZhangSan</name>
            <phone>09098768</phone>
            <host>
                <e:name>LiSi</e:name>
                <e:phone>89675412</e:phone>
            </host>
        </customer>
    语法
        xmlns:[prefix]=”[URI of namespace]”


dtd文件路径解析
    system声明方式:根据给出URL寻找DTD?DTD通过URL显式地直接定位。
    public声明方式:查找众所周知词汇表,应用程序自行确定从数据库中定位dtd,或是从声明的网站中下载(存入数据库缓存中)。
            # myeclipse中通过xml Catalog选项来定位dtd约束

schema文件路径解析
    xsd文件中:targetNamespace="http://www.w3school.com.cn"        # 定义自己的uri标识空间名
    xml文件中:xsi:schemaLocation="http://www.w3school.com.cn note.xsd"                # 指定约束文件的uri地址,一般是空间名加/文件名(目前只有cxf的jaxws约束文件例外【把路径换成了包名】)

dtd #

注意
    同名元素只能用命名空间来区分定义

例子
    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE poem[
    <!ELEMENT poem (author, title, content)>
    <!ELEMENT author (#PCDATA)>
    <!ELEMENT title (#PCDATA)>
    <!ELEMENT content (#PCDATA)>
    ]>

    <poem>
    <author>王维</author>
    <title>鹿柴</title>
    <content>空山不见人, 但闻人语声, 返景入深林,复照青苔上。</content>
    </poem>
    外部引用
            <?xml version='1.0' encoding='utf-8'?>
            <!DOCTYPE poem SYSTEM "outer.dtd">

            // outer.dtd
            <?xml version="1.0" encoding="utf-8"?>
            <!ELEMENT poem (author, title, content)>
            <!ELEMENT author (#PCDATA)>
            <!ELEMENT title (#PCDATA)>
            <!ELEMENT content (#PCDATA)>
语法
    <!ELEMENT author (#PCDATA)> 之中的两个空格必须要有

元素类型
    EMPTY                        # 可以有属性
    ANY                                # 根元素设为ANY类型后,元素出现的次数和顺序不受限制
    #PCDATA
    纯元素类型
    混合类型                        # 可以是元素与内容的混合
        例子
                <!ELEMENT 家庭 (人+, 家电*)>
        修饰符说明
                ()                # 用来给元素分组
                        如
                                (古龙|金庸|梁羽生), (王朔|余杰), 毛毛
                |                # 选择一个
                        如
                                (男人|女人)
                +                # 出现一或多次
                        如
                                (成员+)
                *                # 出现零或多次
                ?                # 出现零或一次
                ,                # 对象必须按指定的顺序出现
                        如
                                (西瓜, 苹果, 香蕉)
属性类型

schema #

Schema约束
    tld文件是Schema约束的
引入
    根元素添加
            文件books.xsd
            <xs:schema        xmlns:xs="http://www.w3.org/2001/XMLSchema"                                // ns  是 namespace
                    targetNamespace="http://www.jnb.com"                        // 给当前约束文件起一个名字
                    elementFormDefault="qualified">                                        // 添加属性,qualified指所有都来自xs空间
                            可选attributeFormDefault="unqualified"                                // unqualified        默认来自的空间
            文件books.xml
            从根元素开始约束
            <jnb:书架 xmlns:jnb="http://www.jnb.com"                                        // 在被约束文件根元素添加属性,约束命名空间
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        // 找到xsi
                    xsi:schemaLocation="http://www.jnb.com books.xsd">        // 指定对应约束文件的物理地址
                    <jnb:书>
                    </jnb:书>
            </jnb:书架>                                                                                                // 在被约束所有元素添加指定空间前缀
            // 保存并检验
语法
    元素约束
    <xs:element name="xxx" type="yy" defaule="" />                                                        // name 元素名 type 类型
    元素类型
            xs:string                // 字符串
            xs:decimal                // big decimal 数字类型
            xs:integer                //
            xs:boolean
            xs:date                        // 日期
            xs:time                        // 时间
    属性约束,在元素标签体里
            <xs:attribute name="" type="" />                                                                        // name 属性名 type 类型
    属性类型和元素类型一样
    属性说明
            <xs:attribute use="required" />
    限定约束
            对值的限定
            枚举限定
            正则限定
            选择限定
            长度限定

    混合内容
            <xs:complexType mixed="true">                        // 只有文本和子元素,mixed可以不使用
            </xs:complexType>

    指示器
            All
            Choice                                                                // 选择出现
            Sequence                                                        // 按声明顺序出现
            maxOccurs
            minOccurs
            Group name
            attributeGroup name
            <xs:all>                                                        // 指定以下元素随意出现
            </xs:all>

uml #

Unified Modeling Languag
静态模型
    用例图(需求分析):客户看           # 项级图(突出重点),一级图,二级图,三级图
        参与者(泛化)
        (关联)
        用例(包含,扩展【继承】)                # 是动词,表示功能模块
        注释
    类图:类结构、类关系(可以自动生成java类)
        继承
        实现
        关联(全局变量)【导航性:一对多关系等】【聚合(所有引用),组合(生命周期相同,如int类型属性)】
            # spring 中的依赖注入是关联
        依赖(局部变量)
    实体关系图ER
动态模型
    时序图:可以根据时序图写代码
        # rose工具中时序图中加入参与者:用例图中创建参与者,拖入时序图中

plantUML #

分类
    salt
        wireframe
    uml
        activity
        class
        component
        sequence
        state
        use case

mermaid.js #

  • 流程图(flowchart)
        graph TD;
            A-->B;
            A-->C;
            B-->D;
            C-->D;
        
  • 时序图(sequence diagram)
        sequenceDiagram
            participant Alice
            participant Bob
            Alice->>John: Hello John, how are you?
            loop Healthcheck
                John->>John: Fight against hypochondria
            end
            Note right of John: Rational thoughts 
    prevail! John-->>Alice: Great! John->>Bob: How about you? Bob-->>John: Jolly good!
  • 甘特图(gantt diagram)
        gantt
            dateFormat  YYYY-MM-DD
            title Adding GANTT diagram functionality to mermaid
    
            section A section
            Completed task            :done,    des1, 2014-01-06,2014-01-08
            Active task               :active,  des2, 2014-01-09, 3d
            Future task               :         des3, after des2, 5d
            Future task2               :         des4, after des3, 5d
    
            section Critical tasks
            Completed task in the critical line :crit, done, 2014-01-06,24h
            Implement parser and jison          :crit, done, after des1, 2d
            Create tests for parser             :crit, active, 3d
            Future task in critical line        :crit, 5d
            Create tests for renderer           :2d
            Add to mermaid                      :1d
        
  • 类图(class diagram)
        classDiagram
        Class01 <|-- AveryLongClass : Cool
        Class03 *-- Class04
        Class05 o-- Class06
        Class07 .. Class08
        Class09 --> C2 : Where am i?
        Class09 --* C3
        Class09 --|> Class07
        Class07 : equals()
        Class07 : Object[] elementData
        Class01 : size()
        Class01 : int chimp
        Class01 : int gorilla
        Class08 <--> C2: Cool label
        
  • git graph
        gitGraph
        options
        {
            "nodeSpacing": 150,
            "nodeRadius": 10
        }
        end
        commit
        branch newbranch
        checkout newbranch
        commit
        commit
        checkout master
        commit
        commit
        merge newbranch
        

markdown #

标题
    # 到 ######开头    # 分别表示一号 到 六号 标题

字体
    **a**或__a__   # 加粗
    *a*或_a_ # 斜体
    ***a***或___a___ # 加粗斜体
    ~~a~~   # 删除线
格式
    换行 结尾两空格 或 <br/>

引用
    >a
    >>b # 不断增加>来多层引用

分割线
    --- # 三个及以上
    *** # 三个及以上,与---显示无分别

图片
    ![alt内容](url "title内容") # alt显示在图片下,title在鼠标悬停时显示

超链接
    [文本](url)
    <a href="url" target="_blank">文本</a>    # 可以用a标签指定target,在新页面显示

列表
    -或+或*开头 # 无序列表-
    数字加点开头  # 有序列表,行数自动
    多空格(至少两个)加列表开头  # 嵌套列表

表格
    标题1|标题2|标题3 # 默认居中对齐
    -|-|-   # -可以多个,:-控制标题和内容左对齐,:-:居中,-:右对齐
    1|2|3   # 默认左对齐

代码
    `a + b;`    # 单选代码
    ```
    function a(){
    }
    ``` # 多行代码
        # tab开头


流程图
    略

org-mode #

restructedText #

LaTeX #

KaTeX #