环境
Windows10 专业版
IntelliJ IDEA Community Edition
jdk15.0.1
程序目的
设置不同日期标签。
选定日期标签 -> 在下方列表显示指定日期的若干图片及文字,切换日期刷新列表。
图片及文字从网络加载。
发现过程
打开任务管理器观察程序内存使用情况。
发现内存占用随着切换日期标签不断增大,基本不会减少。
寻找问题
查看加载图片代码:
public void run() {
try {
URL url = new URL(VIDEO_INFO.image);
ImageIcon imageIcon = new ImageIcon(url);
imageIcon.setImage(imageIcon.getImage()
.getScaledInstance(IMAGE_WIDTH, IMAGE_HEIGHT, Image.SCALE_FAST));
imageLabel.setIcon(imageIcon);
imageLabel.updateUI();
} catch (MalformedURLException e) {
throw new RuntimeException("获取图片失败,url:" + VIDEO_INFO.image + "\n" + e);
}
}
通过网络搜索初步判定为URL缓存复用没有销毁。
将代码中URL设置为不缓存。
public void run() {
try {
URL url = new URL(VIDEO_INFO.image);
URLConnection urlConnection = url.openConnection();
urlConnection.setDefaultUseCaches(false);//设置默认不使用缓存
urlConnection.setUseCaches(false);//设置不使用缓存
ImageIcon imageIcon = new ImageIcon(url);
imageIcon.setImage(imageIcon.getImage()
.getScaledInstance(IMAGE_WIDTH, IMAGE_HEIGHT, Image.SCALE_FAST));
imageLabel.setIcon(imageIcon);
imageLabel.updateUI();
} catch (IOException e) {
throw new RuntimeException("获取图片失败,url:" + VIDEO_INFO.image + "\n" + e);
}
}
观察任务管理器发现改动无效,内存还是会不断增大。
遂进入 JLabel.setImage(Icon) 方法查看,无果。
接着进入 new ImageIcon(URL) 查看,发现端倪
代码如下:
public ImageIcon (URL location) {
this(location, location.toExternalForm());
}
public ImageIcon(URL location, String description) {
image = Toolkit.getDefaultToolkit().getImage(location);
if (image == null) {
return;
}
this.location = location;
this.description = description;
loadImage(image);
}
发现 Image 的获取是通过
Toolkit.getImage(URL);
查看文档:
- public abstract Image getImage(URL url)
- 返回从指定的URL获取像素数据的图像。 由指定的URL引用的像素数据必须采用以下格式之一:GIF,JPEG或PNG。 底层工具包尝试将具有相同URL的多个请求解析为相同的返回图像。
由于促进共享
Image
对象所需的Image
可能会继续保持不再使用无限期的图像,因此鼓励开发人员通过使用createImage
变体实现自己的缓存图像。 如果指定URL存储的图像数据发生变化,则Image
方法返回的Image
对象可能仍然包含在先前调用之后从URL中获取的过期信息。 以前加载的图像数据可以通过在返回的Image
上调用flush
方法手动Image
。此方法首先检查是否安装了安全管理器。 如果是这样,该方法使用url.openConnection()。getPermission()权限调用安全管理器的
checkPermission
方法,以确保允许对该映像的访问。 为了与1.2之前的安全管理员兼容,如果访问被拒绝使用FilePermission
或SocketPermission
,该方法会抛出SecurityException
如果相应的1.1型SecurityManager.checkXXX方法也拒绝许可。参数
url
- 用于获取像素数据的URL。结果
从指定的URL获取其像素数据的图像。
异常
SecurityException
- 如果安全管理器存在,并且其checkPermission方法不允许操作。
- 返回从指定的URL获取像素数据的图像。 由指定的URL引用的像素数据必须采用以下格式之一:GIF,JPEG或PNG。 底层工具包尝试将具有相同URL的多个请求解析为相同的返回图像。
终于找到问题!
解决问题
根据文档所说,使用 Toolkit.createImage(URL) 获取 Image。
private ImageIcon getImage_toolKit_createImage(URL url) {
Image image = Toolkit.getDefaultToolkit().createImage(url);
return new ImageIcon(image);
}
也可以自己从流中读取字节创建Image。
private ImageIcon getImage_readBytes(URL url) {
try {
URLConnection urlConnection = url.openConnection();
int length = urlConnection.getContentLength();
InputStream inputStream = urlConnection.getInputStream();
byte[] bytes = new byte[length];
byte[] readBytes = new byte[SINGLE_READ_LENGTH];
int mark = 0;
int readLength;
while ((readLength = inputStream.read(readBytes)) != -1) {
for (int i = 0; i < readLength; i++, mark++) {
bytes[mark] = readBytes[i];
}
}
inputStream.close();
return new ImageIcon(bytes);
} catch (IOException e) {
return null;
}
}