因此,我开发了一个Android应用程序,作为我的Android游戏的服务器(两个独立的应用程序),这两个应用程序都是用Java编写的。以前,我在日志上收到的消息如下:“跳过2000帧。主线程可能是工作过度”。这是我的应用程序的代码,它只由MainActivity:
我试着引入并发线程来减轻主线程的负担。现在跳过的n帧消息不再显示,但仍然会显示如下消息。“分配并发复制GC已释放”,“启动阻塞GC分配”,“等待阻塞GC分配”,“WaitForGcToComplete阻塞了HeapTrim上的分配”,并且所有这些操作都以抛出内存不足错误结束无法分配32字节的分配,其中15604408个可用字节和14 MB直到OOM,目标占用空间为268435456,增长限制为268435456;因碎片而失败(可能的最大连续分配为0字节)”(VmSize 5539048 kB)。
我尝试释放一些对象(包含“x = null”的代码行),但没有解决。此外,我检查了日志,如果有某种死循环,但它似乎不是这样。
public class MainActivity extends AppCompatActivity {
private static ActivityMainBinding binding;
private WSocServer server;
private int port = 8080;
private boolean isOnline = false;
private static ArrayList<String> logs = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
binding.openConnection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!isOnline) {
isOnline = true;
server = new WSocServer(port);
server.start();
Toast toast = Toast.makeText(view.getContext(),"Server is on", Toast.LENGTH_LONG);
toast.show();
Log.i("WebSocket Server", "Started on port " + server.getPort());
}
else{
Snackbar snack = Snackbar.make(view ,"We are already online!", Snackbar.LENGTH_INDEFINITE);
snack.setAction("Got it", new View.OnClickListener() {
@Override
public void onClick(View view) {
snack.dismiss();
}
});
snack.show();
}
}
});
binding.closeConnection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isOnline) {
isOnline = false;
logs.clear();
Toast toast = Toast.makeText(view.getContext(),"Server is off", Toast.LENGTH_LONG);
toast.show();
try {
server.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
Snackbar snack = Snackbar.make(view ,"We are already offline!", Snackbar.LENGTH_INDEFINITE);
snack.setAction("Got it", new View.OnClickListener() {
@Override
public void onClick(View view) {
snack.dismiss();
}
});
snack.show();
}
}
});
}
private static void addOnView(){
ConstraintLayout cl = binding.logsView;
Handler h = new Handler(Looper.getMainLooper());
for(int i = 0; i < logs.size(); i++){
TextView tv = new TextView(binding.getRoot().getContext());
tv.setTextSize(16);
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setPadding(40,180*(i+1),40,0);
tv.setText(logs.get(i));
Runnable r = new Runnable() {
@Override
public void run() {
cl.addView(tv);
}
};
h.post(r);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
server.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class WSocServer extends WebSocketServer {
private List<String> matchUsers;
private Integer timerSeconds;
private UUID matchId;
//a key represents a match to which an array of extracted numbers is associated
private Hashtable<String,Integer[]> matchExtractedNumbers = new Hashtable<>();
private Hashtable<String, Collection<WebSocket>> matchClients = new Hashtable<>();
private Hashtable<String,Hashtable<String,ArrayList<String>>> users_scorePerMatch = new Hashtable<>();
private Hashtable<String,WebSocket> clientConnection = new Hashtable<>();
private void initTimer(){
timerSeconds = 60;
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
if(timerSeconds > 0) timerSeconds--;
else {
timerSeconds = 60; timer.cancel(); timer.purge();
}
}
};
timer.schedule(task,0L,1000L);
}
private String UsersListToString(List list){
return list.toString().replace("[","").replace("]","");
}
private Integer[] generateExtractedNumbers(){
Integer[] callerBoard = new Integer[90];
List<Integer> boardPool = new ArrayList<>();
boardPool.addAll(Arrays.asList(IntStream.rangeClosed(1,90).boxed().toArray(Integer[]::new)));
for(int i = 0; i < 90; i++){
int rng = ThreadLocalRandom.current().nextInt(0,90-i);
callerBoard[i] = boardPool.remove(rng);
}
return callerBoard;
}
private void initMatch(){
matchId = UUID.randomUUID();
Integer[] matchBoard = generateExtractedNumbers();
matchExtractedNumbers.put(matchId.toString(),matchBoard);
matchClients.put(matchId.toString(),clientConnection.values());
Hashtable<String,ArrayList<String>> matchData = new Hashtable<>();
for(String user: matchUsers) matchData.put(user,new ArrayList<>());
users_scorePerMatch.put(matchId.toString(), matchData);
}
private Integer getExtractedNumber(String match, Integer turn){
if(turn >= 90) return -1;
Integer[] thisMatchExtractedNumbers = matchExtractedNumbers.get(match);
Integer returning = thisMatchExtractedNumbers[turn];
thisMatchExtractedNumbers = null;
return returning;
}
public WSocServer(int port){
super(new InetSocketAddress(port));
}
public WSocServer(InetSocketAddress address) {
super(address);
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.i("WebSocket(open)", conn.getRemoteSocketAddress().getAddress().getHostAddress() + " entered the room!");
logs.add(conn.getRemoteSocketAddress().getAddress().getHostAddress() + " entered the room!");
matchUsers = new ArrayList<>();
matchUsers.addAll(Arrays.asList("user1","user2","user3","user4","user5"));
}
});
thread.start();
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.i("WebSocket(close)", conn + " has left the room! Reason: " + reason);
logs.add(conn + " has left the room!");
}
});
thread.start();
}
@Override
public void onMessage(WebSocket conn, String message) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
logs.add(message + " from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
Log.i("WebSocket(message)", conn + ": " + message);
MainActivity.addOnView();
if(message.startsWith("username")){
if(matchUsers.size() < 6){
String user = message.replace("username;","");
if(!matchUsers.contains(user)) {
matchUsers.add(user);
clientConnection.put(user,conn);
}
String sending = "matchUsers;" + UsersListToString(matchUsers);
conn.send(sending);
}
else conn.send("errorUsername");
}
else if(message.equals("timerStart")){
initTimer();
if(matchUsers.size() < 6){
String sending = "timeStarter;" + timerSeconds.toString();
conn.send(sending);
}
else conn.send("errorTimer");
}
else if(message.equals("getMatchId")){
if(!matchUsers.isEmpty()){
initMatch();
matchUsers.clear();
}
String sending = "matchId;" + matchId.toString();
conn.send(sending);
}
else if(message.startsWith("inGame")){
String[] fields = message.split(";");
String matchId = fields[1].split("=")[1];
int turn = Integer.parseInt(fields[2].split("=")[1]);
Integer extraction = getExtractedNumber(matchId,turn);
fields = null;
conn.send("extracted=" + extraction.toString());
}
else if(message.startsWith("score")){
String matchId = message.split(";")[1].split("=")[1];
String score = message.split(";")[0].split("=")[1];
WebSocket[] clients = matchClients.get(matchId).toArray(new WebSocket[0]);
String user = "";
Enumeration<String> keys = clientConnection.keys();
String key = keys.nextElement();
while(!key.isEmpty()){
if(clientConnection.get(key) == conn) {
user = key;
break;
}
key = keys.nextElement();
}
keys = null;
Hashtable<String,ArrayList<String>> tmp = users_scorePerMatch.get(matchId);
ArrayList<String> tmp_list = tmp.get(user);
tmp_list.add(score);
tmp.replace(user,tmp_list);
users_scorePerMatch.replace(matchId,tmp);
for(int i = 0; i < clients.length; i++){
clients[i].send("statement;" + user + " got " + score + " with");
}
clients = null;
}
else if(message.startsWith("endMatchData")){
String matchId = message.split(";")[1].split("=")[1];
Hashtable<String,ArrayList<String>> users_ofMatch = users_scorePerMatch.get(matchId);
ArrayList<String> users = new ArrayList<>();
Enumeration<String> e = users_ofMatch.keys();
while(e.hasMoreElements()){
Log.e("endmatchdata","a");
users.add(e.nextElement());
}
e = null;
String sending = "matchEndData;";
for(String user: users) sending += user + "=" + UsersListToString(users_ofMatch.get(user)) + ":";
users_ofMatch = null;
conn.send(sending);
}
else if(message.startsWith("totalEnd")){
String matchId = message.split(";")[1].split("=")[1];
if(matchClients.get(matchId)!=null) {
WebSocket[] clients = matchClients.get(matchId).toArray(new WebSocket[0]);
for (WebSocket client : clients) client.close();
Enumeration<String> e = clientConnection.keys();
boolean exit = false;
while (e.hasMoreElements() && !exit) {
Log.e("totalend", "while");
for (WebSocket client : clients) {
Log.e("totalend", "for");
String tmp = e.nextElement();
if (clientConnection.get(tmp) == client) {
clientConnection.remove(tmp);
exit = true;
break;
}
}
}
e = null; clients = null;
matchClients.remove(matchId);
users_scorePerMatch.remove(matchId);
matchExtractedNumbers.remove(matchId);
}
}
}
});
thread.start();
}
@Override
public void onMessage(WebSocket conn, ByteBuffer message) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.i("WebSocket(message)", conn + ": " + message );
}
});
thread.start();
}
public static void main(String[] args){
}
@Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
}
@Override
public void onStart() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.i("WebSocket", "Server started!");
}
});
thread.start();
}
}
}
编辑:事情似乎发生在匹配结束时(即,如果在onMessage方法中有case,则在message.startsWith(“totalEnd”)或message.startsWith(“endmatchdata”)中
编辑2:我发现addOnView函数写得很糟糕,我把它改成了
private static void addOnView(){
ConstraintLayout cl = binding.logsView;
Handler h = new Handler(Looper.getMainLooper());
final TextView tv = new TextView(binding.getRoot().getContext());
tv.setTextSize(16);
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setPadding(40,180*(logs.size()+1),40,0);
tv.setText(logs.get(logs.size()-1));
Runnable r = new Runnable() {
@Override
public void run() {
cl.addView(tv);
}
};
h.post(r);
h = null;
r = null;
}
它解决了。
1条答案
按热度按时间zdwk9cvp1#
Android OutOfMemoryError的原因有很多,其中一些最常见的内存泄漏原因会生成OutOfMemoryError,包括:
1.非静态的内部类。
1.静态视图、上下文或Activity的应用,使用ActivityManager类标识分配的内存和启用android后分配的内存:largeHeap=“true”.
检查代码并将此行添加到清单中