0%

文章字数:108,阅读全文大约需要1分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.colin.tool.img;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;

/**
* @author colin.cheng
* @date 2022-01-14 19:39
* @since 1.0.0
*/
public class ImageBuilder {

private BufferedImage bufferedImage;
private Graphics graphics;

private Font defaultFont = new Font(null, Font.BOLD, 28);
private Color defaultColor = new Color(120, 120, 120);

public ImageBuilder(int width, int height) {
this(width, height, BufferedImage.TYPE_INT_BGR);
}

public ImageBuilder(File img) throws FileNotFoundException, IOException {
try (final FileInputStream inputStream = new FileInputStream(img)) {
init(ImageIO.read(inputStream));
}
}

public ImageBuilder(BufferedImage bufferedImage) {
init(bufferedImage);
}

public ImageBuilder(int width, int height, int imageType) {
init(new BufferedImage(width, height, imageType));
}

private void init(BufferedImage bufferedImage) {
this.bufferedImage = bufferedImage;
graphics = bufferedImage.getGraphics();
graphics.setColor(this.defaultColor);
graphics.setFont(this.defaultFont);
}

public void drawText(String content, int x, int y) {
graphics.drawString(content, x, y);
}

public void fillRect(int x, int y, int width, int height) {
graphics.fillRect(x, y, width, height);
}

public void drawImg(BufferedImage img, int x, int y, int w, int h) {
graphics.drawImage(img, x, y, w, h, null);
}

public ImageBuilder cut(int x, int y, int w, int h) {
final BufferedImage subImage = bufferedImage.getSubimage(x, y, w, h);
return new ImageBuilder(subImage);
}

public BufferedImage toImage() {
return this.bufferedImage;
}

public boolean writeToFile(File file, String imgType) throws IOException {
return ImageIO.write(this.bufferedImage, imgType, file);
}


// setter

public void setFont(Font font) {
graphics.setFont(font);
}

public void setColor(Color color) {
graphics.setColor(color);
}

public int getWidth() {
return bufferedImage.getWidth();
}

public int getHeight() {
return bufferedImage.getHeight();
}

// static
}
  • 视频截取关键帧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.colin.tool.img;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
* @author colin.cheng
* @date 2022-01-17
* @since 1.0.0
*/
public class ImageUtil {

public static ImageBuilder getComicStrip(BufferedImage[] imgArray, int outHeight) {
int showW = outHeight, showH = outHeight;
final ImageBuilder imageBuilder = new ImageBuilder(showW * imgArray.length, showH, Color.BLACK);
for (int i = 0; i < imgArray.length; i++) {
BufferedImage image = imgArray[i];
final int height = image.getHeight();
final int width = image.getWidth();
if (height == width) {
imageBuilder.drawImg(image, i * showW, 0, showW, showH);
} else {
if(height > width) {
double n = (double) height / showH;
double w = width / n;
imageBuilder.drawImg(image, (int)(showW * i + (showH - w) / 2), 0, (int)w, showH);
} else {
double n = (double) width / showW;
double h = height / n;
imageBuilder.drawImg(image, showW * i, (int)((showW - h) / 2), showW, (int)h);
}
}
}
return imageBuilder;
}

public static ImageBuilder addComicStripIndex(BufferedImage comicStrip, float transparency, int strokeWidth) {
final int width = comicStrip.getWidth(), height = comicStrip.getHeight(), offset = height;
int length = width / height;
final ImageBuilder imageBuilder = new ImageBuilder(width + offset, height);
BufferedImage banner = ImageBuilder.cut(comicStrip, 0, 0, height, height);
imageBuilder.drawImg(banner, 0, 0, height, height);
imageBuilder.drawImg(comicStrip, offset, 0, width, height);
for (int i = 0; i < length; i++) {
double w = height - strokeWidth, base = width / w, h = height / base;
final int x = height * i + offset + strokeWidth / 2, y = (int)(height - h);
imageBuilder.drawTranslucentImage(comicStrip, x, y, (int)w, (int)h, transparency);
// 画框
if(strokeWidth > 0) {
imageBuilder.setColor(Color.BLACK);
imageBuilder.drawRect(x + (i * (int)h), y, (int)h, (int)h, strokeWidth);
}
// 当前所在的关键帧变清晰
if(transparency < 1) {
BufferedImage frame = ImageBuilder.cut(comicStrip, height * i, 0, height, height);
imageBuilder.drawImg(frame, x + (i * (int)h), y, (int)h, (int)h);
}
}
return imageBuilder;
}
}

文章字数:69,阅读全文大约需要1分钟

语言包下载

依赖

1
2
3
4
5
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>tesseract-platform</artifactId>
<version>4.1.1-1.5.5</version>
</dependency>

代码

  • 只测试了简体中文chi_sim和英文eng
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.colin.tool.img;

import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.leptonica.PIX;
import org.bytedeco.leptonica.global.lept;
import org.bytedeco.tesseract.TessBaseAPI;

/**
* @author colin.cheng
* @date 2022-01-17
* @since 1.0.0
*/
public class ImageUtil {

public static String ocr(String imagePath, OcrLanguage language) {
TessBaseAPI api=new TessBaseAPI();
if (api.Init(language.getPath(), language.getLangName()) != 0) {
api.End();
return "";
}
PIX image = lept.pixRead(imagePath);
if (image == null) {
return "";
}
api.SetImage(image);
BytePointer outText=api.GetUTF8Text();
String result=outText.getString();
api.End();
outText.deallocate();
lept.pixDestroy(image);
return result;
}

public static void main(String[] args) {
String text = ocr("F:\\test\\img\\0.jpg", OcrLanguage.ZH_CN);
System.out.println(text);
}

public enum OcrLanguage {
/** 简体中文 */
ZH_CN("chi_sim", "F:\\test\\img\\traineddata"),
/** 英文 */
EN("eng", "F:\\test\\img\\traineddata");

/** 文件名 */
private String langName;
/** 所在文件夹 */
private String path;

OcrLanguage(String langName, String path) {
this.langName = langName;
this.path = path;
}

public String getLangName() {
return langName;
}

public String getPath() {
return path;
}
}
}

文章字数:812,阅读全文大约需要3分钟

JIT(Just in time compilation)及时编译,能够让java程序执行速度提升

优化手段

  1. cpu编译优化:针对特定的处理器型号指令集编译热点代码,提升性能
  2. 减少查表次数:调用Object.equals()方法,在运行时发现一直是在调用Stringequals()编译后直接调用String.equals()
  3. 逃逸分析:变量默认分配在主存上,但是方法中变量未逃出使用的生命周期则可以考虑在栈上分配内存。如果是对象则可以标量替换(对象替换成相应字段的变量)
  4. 寄存器分配:部分变量可以分配在寄存器中,相对主存,读取性能提升。
  5. 热点代码机器码缓存:缓存热点代码的机器码,但是缓存空间有固定大小,到达上限就不能缓存了
  6. 方法内联:热点方法的内部调用的方法会被复制到调用方法里,减少调用开销。

方法内联

  1. 条件
1
2
3
4
5
6
7
//次数限制
client编译器时到达1500此执行会标记热点方法,执行内联
server编译器时到达10000此执行会标记热点方法,执行内联
值可以由 -XX:CompileThreshold 设置
//大小限制
如果方法是经常执行的,默认情况下,方法大小小于325字节的都会进行内联(可以通过** -XX:MaxFreqInlineSize=N**来设置这个大小)
如果方法不是经常执行的,默认情况下,方法大小小于35字节才会进行内联(可以通过** -XX:MaxInlineSize=N **来设置这个大小)
  1. 建议
1
2
3
1.更小的方法体,JVM总是偏好更小的方法。
2.尽量使用final、private、static修饰符
3.使用+PrintInlining参数校验效果

逃逸分析

变量在HotSpot VM中逃逸分析的类别分为

  1. NoEscape:未逃逸,可以进行标量替换(用局部变量替换对象字段)
  2. ArgEscape:作为参数被传入其它方法中,此情况下如果被调用的方法和调用方法产生方法内联,也可以进行标量替换。
  3. GlobalEscape:无法标量替换

文章字数:737,阅读全文大约需要2分钟

一、jdk自带的javap工具

avap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译。相对于jd, javap能够查看java编译器为我们生成的字节码,有利于程序调优。

使用方式:(命令行状态下)

1
2
javap <选项> <类名>
常用: javap -c -v 类名

部分常用命令(1.8)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-l  打印行和本地变量表
-public 只显示公共类和成员变量
-protected 只显示公共的、受保护的类和变量
-private 显示所有的类和成员变量
-p 显示所有的类和成员变量
-Joption 给jvm指定参数,例如:
javap -J-version
javap -J-Djava.security.manager -J-Djava.security.policy=MyPolicy
-s 打印内部类型签名(用于泛型)
-sysinfo 打印被处理类的系统信息(路径,大小,日志,md5哈希)
-constants 显示static final常量
-c 输出分解后的代码。例如,类中每一个方法内,包含java字节码的指令
-verbose 输出栈大小,变量(locals)和方法参数的个数
-classpath path 指定查询类的路径.如果设置该值,则会覆盖默认值和CLASSPATH环境变量。
-bootclasspath path 指定启动类的路径,默认值是jre/lib/rt.jar 和 其他少数的jar.
-extdir dirs 覆盖 java.ext.dirs路径

Fernflower.jar

批量反编译java字节码,idea使用的就是这种方法。、
使用

  • 首先把工程的所有class 压缩成zip
  • 执行命令
    1
    2
    # 二、java -jar fernflower.jar  <目标>.jar <文件夹名>/
    java -jar fernflower.jar classes.zip demo/

三、java.lang.instrument.Instrumentation

java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。
能够使加载的类与物理保存的.class文件不同,热部署的实现支持之一。常用于累的监控。
就是使用代理类,类加载前会执行代理类的指定方法处理字节码。

四、三种工具和asm的关系

  • asm是用来解析,保存,修改,创建二进制class文件的工具
  • javapFernflower.jar是反编译java class文件的工具,只是各有特点。
  • Instrumentation可以代理类,在加载前使用asm修改类。(JavaAgent java探针、代理)的实现

    Instrumentation可以编写代码使用javaagent代理执行其它java文件,在代码里使用上面的asm库动态修改代码。也可以直接将所有字节保存,并加载其它class文件。


文章字数:125,阅读全文大约需要1分钟

sql的脚本生成了数据,再使用jpa修改数据时出现报错。

分析

  1. 看提示发现是Integer报了空指针的异常。查看出错位置
    IntegetType.class

    1
    2
    3
    public Integer next(Integer current, SessionImplementor session) {
    return current + 1;
    }
  2. 查看调用栈,大致定位到jpa在进行CAS修改操作时旧版本号的数据没找到。
    Versioning.class

    1
    2
    3
    4
    5
    6
    7
    8
    public static Object increment(Object version, VersionType versionType, SessionImplementor session) {
    Object next = versionType.next(version, session);
    if (LOG.isTraceEnabled()) {
    LOG.tracef("Incrementing: %s to %s", versionType.toLoggableString(version, session.getFactory()), versionType.toLoggableString(next, session.getFactory()));
    }

    return next;
    }
  3. 查看Model的定义,发现有一个被@version注解的字段没有被赋值。

解决

在脚本中添加上版本字段的值


文章字数:225,阅读全文大约需要1分钟

dump是将虚拟机运行时的快照,将运行时信息保存到文件。分为线程dump纯文本、堆dump包含线程dump和堆对象,二进制。

常用命令

jdkbin目录下,包含java命令及常用工具

  1. jps: 查看本机的java中进程信息
  2. jstack: 打印线程的栈信息,制作线程dump
  3. jmap: 打印内存映射,制作堆dump
  4. jstat: 性能监控工具
  5. jhat: 内存分析工具
  6. jconsole: 简易的可视化控制台
  7. jvisualvm: 功能强大的控制台

制作java dump

  1. 虚拟机发送内存不足错误时,自动生成堆dump
1
-XX:+HeapDumpOnOutOfMemoryError
  1. 图形化工具制作dump
    使用jdk自带的工具Java VisualVM

  2. 命令行制作

  • jstack: 打印线程的栈信息,制作线程dump
  • jmap: 打印内存映射,制作堆dump

查看进程pid

1
ps -ef | grep java

文章字数:476,阅读全文大约需要1分钟

浏览器访问java后端的页面,然后一直在转圈圈。很久了也没有反应

解决过程

  1. 首先一直转圈圈肯定是http连接没有设置超时
  2. 查看java的日志,发现没有任何日志输出。
  3. 平常即使什么都不做也会有一下定时任务偶尔输出一些信息,但是这次却异常平静
  4. 看了一下最新写的代码是获取redis信息
1
redisTemplate.getConnectionFactory().getConnection().info()
  1. 刚写的功能是定时访问这个接口获取redis信息,并且输出信息到前端。
  2. 这里从connectionFactory获取了连接,但是会不会自动释放。看了一下源代码,似乎没有释放的环节。
  3. 查看redisTemplate代码,看看执行其他命令的时候是怎么获取到连接的

execute方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = this.getConnectionFactory();
RedisConnection conn = null;

Object var11;
try {
if (this.enableTransactionSupport) {
conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}

boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}

RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
T result = action.doInRedis(connToExpose);
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}

var11 = this.postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}

return var11;
}

RedisConnectionUtils.getConnection(factory);的内部实现也是getConnectionFactory().getConnection(),所以可以知道确是没有自动释放,需要手动释放一下

  1. 于是模仿源代码的形式获取连接,然后执行,释放
1
2
3
4
5
6
7
8
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
redisInfo = conn.info();
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
  1. 再次进行之前的操作,发现无响应的问题解决了。

分析

现在看来无响应应该就是获取redis连接的代码堵塞了,循环导致被占用的连接数越来越多,直到连接池被沾满。这也解释了为什么后台无信息输出,因为很多的线程也是依赖redis


文章字数:106,阅读全文大约需要1分钟

乐观锁默认读之后不会有修改,于是不上锁。但是更新的时候会判断期间有没有修改数据。悲观锁总是设想最坏的情况,读之后都会有人修改数据,于是拿数据的时候就会上锁。

悲观锁

  1. synchronizedcpu级别的锁机制
  2. sqllock tables

乐观锁

  1. CAS(Compare and Swap 比较并交换)
  2. jpa@Version

文章字数:103,阅读全文大约需要1分钟

  1. Vert.x是一款依托于Netty的事件驱动的框架,相对于传统的服务器架构,更加轻量级,高性能。

  2. springWebFlux是随着Spring 5推出的响应式web框架。基于HTTP/Reactive Streams,运行环境需要支持异步运行的环境,如NettyUndertow。开发模式上支持Spring WebMVC风格的注解@Controller RequestMapping和java8的lambda风格函数。


文章字数:149,阅读全文大约需要1分钟

Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//B应用为服务端:将udp监听放到一个线程中,当有客户端请求时就会进行响应
/**
* udp连接,用于动态ip, pos向255.255.255.255:5060发送请求即可
* **/
public class UdpServer extends Thread implements Runnable {
private final int MAX_LENGTH = 1024;
private final int PORT = 5060;
private DatagramSocket datagramSocket;

public void run() {
try {
init();
while(true){
try {
byte[] buffer = new byte[MAX_LENGTH];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
receive(packet);
String receStr = new String(packet.getData(), 0 , packet.getLength());
System.out.println("接收数据包" + receStr);
byte[] bt = new byte[packet.getLength()];

System.arraycopy(packet.getData(), 0, bt, 0, packet.getLength());
System.out.println(packet.getAddress().getHostAddress() + ":" + packet.getPort() + ":" + Arrays.toString(bt));
packet.setData(bt);
response(packet);

} catch (Exception e) {
e.printStackTrace();
LoggerUtil.error("udp线程出现异常:" + e.toString());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

public void receive(DatagramPacket packet) throws Exception {
datagramSocket.receive(packet);
}

public void response(DatagramPacket packet) throws Exception {
datagramSocket.send(packet);
}

/**
* 初始化连接
*/
public void init(){
try {
datagramSocket = new DatagramSocket(PORT);
System.out.println("udp服务端已经启动!");
} catch (Exception e) {
datagramSocket = null;
System.out.println("udp服务端启动失败!");
e.printStackTrace();
}
}
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/***
* UDP Client端
***/
public class UdpClient {

private String sendStr = "hello";
private String netAddress = "255.255.255.255";
private final int PORT = 5060;

private DatagramSocket datagramSocket;
private DatagramPacket datagramPacket;

public UdpClient(){
try {
datagramSocket = new DatagramSocket();
byte[] buf = sendStr.getBytes();
InetAddress address = InetAddress.getByName(netAddress);
datagramPacket = new DatagramPacket(buf, buf.length, address, PORT);
datagramSocket.send(datagramPacket);

byte[] receBuf = new byte[1024];
DatagramPacket recePacket = new DatagramPacket(receBuf, receBuf.length);
datagramSocket.receive(recePacket);

String receStr = new String(recePacket.getData(), 0 , recePacket.getLength());

       //获取服务端ip
String serverIp = recePacket.getAdress();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket
if(datagramSocket != null){
datagramSocket.close();
}
}
}

public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
UdpClient udpClient = new UdpClient();
}
}).start();
}
}
}