Java是一种跨平台的语言,JVM屏蔽了底层系统的不同,为Java字节码文件构造了一个统一的运行环境。
Java 如何实现在不同操作系统、不同硬件平台上,都可以不用修改代码就能顺畅地执行?
计算机领域的任何问题都可以通过增加个中间层(虚拟层)来解决
Java所有的指令有200个左右,一个字节(8位)可以存储256种不同的指令信息,一个这样的字节称为字节码(Bytecode)。在代码的执行过中,JVM将字节码解释执行屏蔽对底层操作系统的依赖,JVM也可以将字节码编译执行,如果是热点代码,会通过JIT动态地编译为机器码,提高执行效率。
低层次的当前类加载器,不能覆盖更高层次类加载器已经加载的类。如果低层次的类加载器想加载一个未知类,需要上级类加载器确认,只有当上级类加载器没有加载过这个类,也允许加载的时候,才让当前类加载器加载这个未知类。
Java 中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的引用而已。
方法区主要存放从磁盘加载进来的类字节码,而在程序运行过程中创建的类实例则存放在堆里。程序运行的时候,实际上是以线程为单位运行的,当JVM进入启动类的main方法的时候,就会为应用程序创建一个主线程,main方法里的代码就会被这个主线程执行,每个线程有自己的Java栈,栈里存放着方法运行期的局部变量。而当前线程执行到哪一行字节码指令,这个信息则被存放在程序计数寄存器。
package com.bjsxt.test;public class Demo01 { public static void main(String[] args) A a = new A(); System.out.println(A.width); }}class A { public static int width=100; static{ System.out.println("静态初始化类A"); width=300; } public A(){ System.out.println("创建A类的对象"); }}
所有在方法内定义的基本类型变量,都会被每个运行这个方法的线程放入自己的栈中,线程的栈彼此隔离,所以这些变量一定是线程安全的。
Java内存模型规定在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内存拷贝主内存变量副本来进行。
一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
JVM垃圾回收就是将JVM堆中的已经不再被使用的对象清理掉,释放宝贵的内存资源。
JVM通过一种可达性分析算法进行垃圾对象的识别,具体过程是:从线程栈帧中的局部变量,或者是方法区的静态变量出发,将这些变量引用的对象进行标记,然后看这些被标记的对象是否引用了其他对象,继续进行标记,所有被标记过的对象都是被使用的对象,而那些没有被标记的对象就是可回收的垃圾对象了。
进行完标记以后,JVM就会对垃圾对象占用的内存进行回收,回收主要有三种方法
-XX:MaxGCPauseMillis
标准参数,所有的JVM实现都必须实现这些参数的功能,而且向后兼容
非标准参数,默认jvm实现这些参数,但不保证所有jvm实现都实现,且不保证向后兼容
·-Xms初始堆大小
·-Xmx最大堆大小
·-Xmn新生代大小
·-Xss线程堆栈大小
非Stable参数,此类参数各个jvm实现会有所不同,将来可能会随时取消
·-XX:-UseConcMarkSweepGC 启用CMS垃圾回收
基本工具:JPS,JSTAT,JMAF
集成工具:JConsole,JVisualVM
JPS 用来查看 host 上运行的所有 java 进程的 pid (jvmid),一般情况下使用这个工具的目的只是为了找出运行的jvm进程ID,即lvmid,然后可以进一步使用其它的工具来监控和分析JVM。
常用的几个参数:
JSTAT("Java Virtual Machine statistics monitoring tool")是JDK自带的一个轻量级小工具。主要对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
语法结构如下:jstat [Options] vmid [interval] [count]
S0 -- Heap上的 Survivor space 0 区已使用空间的百分比
S1 -- Heap上的 Survivor space 1区已使用空间的百分比
E -- Heap上的 Eden space 区已使用空间的百分比
O -- Heap上的 Old space 区已使用空间的百分比
YGC -- 从应用程序启动到采样时发生 Young GC 的次数
YGCT -- 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC -- 从应用程序启动到采样时发生 Full GC 的次数
FGCT -- 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT -- 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
JMAP是一个可以输出所有内存中对象的工具,甚至可以将VM中的heap,以二进制输出成文本。
使用方法
使用场景(1/0阻塞,多CPU并发)
资源争用与同步问题
java.util.concurrent
启动线程数=[任务执行时间/(任务执行时间−IO等待时间)]∗CPU内核数
在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。
在临界区中使用适当的同步就可以避免竞态条件。
允许被多个线程安全执行的代码称作线程安全的代码。
局部变量
局部的对象引用
对象成员
Java Web应用的多线程从哪儿来的?
Servlet是线程安全的吗?
创建一个 Threadloca|变量
存储此对象的值
读取一个 Threadlocal对象的值:
public void set(T value){ Thread t = Thread.currentThread(); ThreadLocalMap map=getMap(t); if (map != null) map.set(this,value); else createMap(t,value);}void createMap (Thread t, T firstValue) { t.threadlocals=new ThreadLocalMap(this,firstValue);}
Java内存泄漏是由于开发人员的错误引起的。
如果程序保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存。
合理使用线程池和对象池
使用合适的JDK容器类(顺序表,链表,Hash)
缩短对象生命周期,加速垃圾回收
使用I/O buffer及NIO
优先使用组合代替继承
合理使用单例模式
虚拟化所有层次
XXXX网站的正常流量情况
高并发下的风险
高并发下的事故
秒杀
商业需求
技术挑战
服务器准备(距秒杀开始仅五天时间来不及采购)
带宽准备
1.图片网络带宽:1.0G
新增图片带宽:必须控制在1.0G左右
每件商品秒杀页面的图片总大小不得超过:1000000/(1000*8)=125K/每商品
2.网站并发:
单件商品并发:1000【来自运营的预估】
总并发:8(件商品)*1000(人/商品)=8000
简单系统:
三个页面组成:秒杀商品列表,秒杀商品介绍,下单
静态化
并发控制,防秒杀器
简化流程
前端优化
秒杀商品list和Detail是静态HTML页面
秒杀商品列表/秒杀商品介绍页面,如何判断秒杀开始否。
答案:valid-offer.js
var valid_offerIds=23624364,53778658,35885833,46999696,5006797057
阀门:基于TT的计数器
秒杀 Detail 页面
下单页面:
Mod-jk worker 调优
JkWorkerProperty worker.list=localnodeJkWorkerProperty worker.localnode.port=7011JkworkerProperty worker.localnode.host=localhostJkWorkerProperty worker.localnode.type=ajp13JkWorkerProperty worker.localnode.ibfactor=1 JkWorkerProperty worker.localnode.socket_keepalive=TrueJkWorkerProperty worker.localnode.socket_timeout=20JkWorkerProperty worker.localnode.connection_pool_minsize=25JkWorkerProperty worker.localnode.connection_pool_timeout=600
JBoss AJP Connector
Tomcat APR 设定
图片合并
HTML内容压缩
图片压缩:图片 Bytes < 长*宽/2250
HTML Header Cache-Control 设置
CSS, JS精简
数据库操作:全部砍掉
秒杀流程精简
采用内存缓存
XXXX.com其他页面
交易系统优化
>Style集群:style.XXXX.china.XXXX.com
>图片服务器集群:img.XXXX.china.XXXX.com
>静态页面集群:page.XXXX.china.XXXX.com
>出问题直接把XXXX相关域名卡掉,所有请求跳到万能出错页面。
>5天时间来不及采购服务器,因此SA待命,随时准备将非核心应用集群的兄余服务器下线,加入到秒杀集群。
>所有办法均失效的情况下,例如流量耗尽。
>非核心应用集群统统停止服务,如资讯,论坛,博客等社区系统。
>保住首页,Offer Detail,旺铺页面等核心应用的可用性。
>任何出错都302跳转到此页面
>位于另外集群
88小时秒杀,坚守阵地,大获成功。
秒杀还是被秒杀?终于有了答案
三道阀门设计非常有效,拦住了秒杀器。
采用 Lighttpd 替代 Apache 杀手铜(AIO)
Lighttpd 1.5 VS Apache2.2.4
小页面性能(100K)
大页面性能(10M)
性能关键:Web Server的高性能I/O
XXXX应用服务器升级项目:
Apache2.2+Mod-Proxy+Jetty7.1.5 与XXXX现有架构性能对比:
性能大幅提升,XXXX全站下线1/3应用服务器(100台,明年不用采购新机器)
架构更轻量,配置更简单
应用更无状态化,开发和维护的福音
更加安全
XXXX 青岛镜像站项目
在Offer集群前部署 Squid反向代理集群
XXXX 性能优化领域立项中
(Lucene缺点是不支持分布式,数据量很大时考虑ElasticSearch)
PageRank,网页排名,又称网页级别,Google左侧排名或佩奇排名,是一种由搜索引擎根据网页之间相互的超链接计算的技,而作为网页排名的要素之一,以Google公司创辨人拉里·佩奇(Larry Page)之姓來命名。
PageRank通过网络浩瀚的超链接開像来确定一个页面的等级。Google把从A页面到B页面的链接解释为A页面给B页面投票,Google根据投票来源(甚至来源的来源,即链接到A页面的页面)和投票目标的等级来决定新的等级。简单的说,一个高等级的页面可以使其他低等级页面的等级提升。
一个页面的「得票数」由所有链向它的页面的重要性来决定,到一个页面的超链接相当于对该页投一票。一个页面的PageRank是由所有链向它的页面([链入页面])的重要性经过递归算法得到的。一个有较多链入的页面会有较高的等级,相反如果一个页面没有任何链入页面,那么它没有等级。
假设一个由4个页面组成的小团体:A,B,C和D。如果所有页面都链向A,那么A的PR(PageRank)值将是B,C及D的Pagerank总和。
PR(A)=PR(B)+PR(C)+PR(D)