Android-Java中出现内存不足异常,由于碎片,分配失败

7gcisfzg  于 2023-01-19  发布在  Java
关注(0)|答案(1)|浏览(88)

因此,我开发了一个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;
    }

它解决了。

zdwk9cvp

zdwk9cvp1#

Android OutOfMemoryError的原因有很多,其中一些最常见的内存泄漏原因会生成OutOfMemoryError,包括:
1.非静态的内部类。

  1. getContext()和getApplicationContext()的使用不正确()
    1.静态视图、上下文或Activity的应用,使用ActivityManager类标识分配的内存和启用android后分配的内存:largeHeap=“true”.
    检查代码并将此行添加到清单中
android:largeHeap="true"

相关问题